






















本文其实是之前rust重借用机制与原理的细化,如果已经理解了这篇文章,可以不用再看这篇文章。
为了彻底搞懂这个问题,我们需要抛开具体的复杂业务代码,建立两个最核心的 Rust 底层世界观:
String、Vec 没有本质区别,在内存里也占字节,也有自己的所有权。&mut T(可变引用)是独占的,绝对不允许被复制(Non-Copy)。下面我们用最纯粹、最简单的抽象例子,一步步拆解“为什么可变引用直接传会触发 Move”。
&mut T 会触发 Move在 Rust 中,所有类型在面临“赋值”或“传参”时,只有两条路可以走:要么执行 Copy(复制),要么执行 Move(移动)。
&T:它实现了 Copy。因为多个只读指针指向同一个数据是绝对安全的。&mut T:它没有实现 Copy。因为 Rust 的安全基石是 “独占可变性”(同一时间只能有一个活跃的写指针)。如果 &mut T 允许被 Copy,意味着你可以完美克隆出两个一模一样的指针同时修改同一块内存,这会导致数据竞争。因为 &mut T 不能被 Copy,所以当你把它当成一个普通变量直接传递时,它只能执行 Move。
fn main() {
let mut data = 42;
let r1: &mut i32 = &mut data; // r1 拥有这个可变引用的所有权
let r2 = r1; // 💥 触发了 Move!r1 的所有权转移给了 r2
// println!("{}", r1); // ❌ 编译报错:使用了已被移动的值(use of moved value)
println!("{}", r2); // ✅ 正常,现在 r2 是唯一合法的可变引用
}
既然直接传会触发 Move,那为什么我们在写普通 Rust 代码时,连续把一个可变引用传给两个函数,编译器却不报错呢?
fn print_mut(r: &mut i32) { /* ... */ }
fn foo(mut_ref: &mut i32) {
print_mut(mut_ref); // 👈 看起来是直接传,这里为什么没被 Move?
print_mut(mut_ref); // 👈 如果上面 Move 了,这里为什么还能用?
}
答案是:编译器在暗中帮我们做了一次“偷梁换柱”,这个机制叫 隐式再借用(Implicit Reborrow)。
当编译器看到 print_mut(mut_ref),并且发现你后面还要继续使用 mut_ref 时,它为了不让你写代码太痛苦,会悄悄把你的代码改写成:
print_mut(&mut *mut_ref); // 👈 编译器自动做的“再借用”
*mut_ref:先解引用,找到底层真正的数据。&mut ...:对底层数据重新生成一个短命的、临时的可变引用。在这个过程中,mut_ref 本身的所有权牢牢留在了 foo 函数里。借出去的只是一个临时的“影子引用的所有权”,等 print_mut 执行完,这个影子消失,mut_ref 自动复活。
&mut)既然编译器这么聪明,为什么在某些特定的代码结构(比如函数参数: mut rjson: &mut RapidJson)中,直接传 rjson 会触发 Move,甚至逼得原作者必须手动写 &mut rjson 呢?
有以下几个经典场景会破坏编译器的聪明才智,导致隐式再借用失效,强行触发 Move:
mut 修饰(指针重新绑定)如果参数声明写成了 mut rjson: &mut T(注意变量名前面有 mut):
这告诉编译器:“我接下来不仅要用这个指针,我还可能在当前函数里把这个指针本身改指向别的地方(例如 rjson = &mut other_data;)。”
一旦变量本身是可变的,Rust 编译器在推导生命周期和再借用时就会变得极度保守。它为了确保万无一失,往往会直接放弃隐式再借用,将其视作纯粹的 Move。
在涉及 extern "C" 跨语言边界、非标准的泛型约束、或者多重闭包(Closure)时,Rust 的生命周期分析器(Borrow Checker)无法完美穿透复杂的上下文。一旦它无法确定后续是否安全,就会立刻退化到最基础的语义:&mut T 没有 Copy 特性,一律按 Move 处理。
&mut rjson 到底做了什么?当隐式再借用因为上述原因失效,而你直接传 rjson 又会被 Move 导致后续无法使用时,手动加上 &mut rjson 成了一种强行的破局手段。
如果 rjson 的类型是 &mut T:
&mut(直接写 rjson):你传递的是 &mut T 本身。因为隐式再借用失效,它发生 Move,rjson 变量从此死亡。&mut rjson:你创建了一个全新的指针,类型是 &mut &mut T(指向可变引用的可变引用)。rjson 本身没有任何所有权转移,它安全地留在当前函数中。虽然接收方函数可能只需要 &mut T,但 Rust 面对 &mut &mut T 传入 &mut T 的参数要求时,会触发标准的 Deref(解引用)自动降级,将其剥离为单层引用成功匹配,同时完美保住了 rjson 的所有权。
rjson 本身确实是可变引用。&mut *rjson(再借用)。&mut rjson 是通过显式创建一个临时的“二级指针”,强行把底层 rjson 的所有权锁在当前函数里。参考资料:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。