Axum中克隆静态引用的嵌套值与克隆静态引用本身的行为差异疑问
Axum中克隆静态引用的嵌套值与克隆静态引用本身的行为差异疑问
嘿,这个问题其实戳中了Rust里引用克隆和结构体克隆的核心区别,我来给你掰扯清楚~
首先得抓住核心前提:你的ApiState里的settings是一个&'static Settings——也就是指向静态生命周期Settings实例的引用,这是分析两种写法差异的关键。
先看第一种写法:
pub async fn ui_settings(State(state): State<ApiState>) -> ApiResponse<UiSettings> { OK!(state.settings.api.ui.clone()) }
这里的执行逻辑是这样的:
state.settings是&Settings类型,访问它的api字段时,Rust会自动解引用这个引用,得到&ApiSettings(因为Settings的api字段是值类型)- 接着访问
ui字段,得到&UiSettings - 最后调用
clone(),因为UiSettings派生了Clone trait,这一步会克隆出一个全新的UiSettings实例,拿到它的所有权,刚好匹配返回类型的要求。
再看你写的第二种写法:
pub async fn ui_settings(State(state): State<ApiState>) -> ApiResponse<UiSettings> { let settings = state.settings.clone(); OK!(settings.api.ui) }
这里藏着一个很容易踩的Rust坑:
state.settings是引用类型,Rust会优先调用引用自身的Clone实现,而不是引用指向的Settings结构体的Clone实现。引用的Clone逻辑特别简单——就是返回当前引用的副本,说白了settings变量还是一个&'static Settings,和原来的引用完全指向同一个静态Settings实例。- 那
settings.api.ui自然还是&UiSettings类型,这时候如果你的OK!宏要求传入UiSettings(毕竟返回类型是ApiResponse<UiSettings>),这段代码其实编译不通过,除非你给UiSettings也派生了Copytrait(但你代码里只加了Clone)。
如果咱们把第二种写法修正成真正克隆整个Settings结构体的版本:
let settings = (*state.settings).clone(); OK!(settings.api.ui)
这时候才是真的递归克隆了整个Settings结构体(包括嵌套的ApiSettings和UiSettings),然后返回其中的UiSettings字段。这时候和第一种写法的差异就很明显了:
- 第一种写法只克隆最内层需要的
UiSettings,开销极小 - 修正后的第二种写法会克隆整个嵌套结构体,开销大很多,要是Settings里还有其他大字段,性能影响会更显著。
最后再给你划个重点:
- 你现在写的第二种写法(没显式解引用的)和第一种写法完全不是一回事,甚至可能编译失败
- 要是你想先克隆整个Settings再取字段,必须显式解引用后再克隆,这时候和第一种写法的核心差异就是克隆的范围不同,性能开销天差地别。
内容来源于stack exchange
相关产品推荐
相关产品推荐

