Rust可变引用作用域与所有权规则困惑咨询
Rust可变引用与作用域的疑问解析
问题代码与困惑点
以下代码运行结果与常规理解的Rust可变引用规则不符,存在多处疑问:
// 输出: // string-a // string-aaabc fn main() { let mut a = String::from("string-"); let s = &mut a; let ss = &mut a; // s在此处应该失效? ss.push('a'); // string-a println!("{}", ss.clone()); // ss.clone()会借用共享引用,可变引用ss应失效,但为何仍可用? ss.push_str("aa"); let b = &mut (*ss); // 多次可变借用*ss,ss应失效,但为何仍可用? // 取消注释会报错(cannot borrow `*ss` as mutable more than once at a time) // let b = &mut (*ss); ==> 首次可变借用在此处 ==> 原因? // ss.push('d'); ==> 二次可变借用在此处 ==> 原因? // b.push('b'); ==> 首次借用后续被使用 // 原因? // ss.push('d'); b.push('b'); ss.push('c'); // 为何ss仍可用? println!("{}", a); // string-aaabc }
核心原因:Rust的非 lexical lifetimes (NLL)
Rust 1.31引入的NLL规则,让编译器不再以代码块作为引用作用域的边界,而是精准追踪引用最后一次被使用的位置:只要引用后续没有被使用,编译器就会提前释放它的借用权,而非等到变量声明的代码块结束。
1. 为什么let ss = &mut a;没有导致s失效?
因为s被声明后从未被使用过。NLL规则下,编译器会直接判定s的可变借用没有实际作用,立即释放它的借用权,所以后续ss的可变借用不会和s产生冲突。如果在let ss = &mut a;之后添加s的使用(比如println!("{}", s);),就会触发可变引用冲突的错误。
2. ss.clone()之后,ss为何还能继续使用?
String::clone()的方法签名是fn clone(&self) -> Self,它确实会借用ss的不可变引用,但这个引用的生命周期只存在于clone()调用期间。NLL分析会发现,clone()执行完毕后,这个临时的不可变引用就被释放了,ss的可变引用恢复可用状态,自然能继续调用push_str方法。
3. let b = &mut (*ss);之后,ss为何还能被使用?
b是*ss的可变引用,本质上指向同一个底层String。但当前代码中:
b的最后一次使用是b.push('b');,执行完这行后,b的可变借用权被释放。- 后续的
ss.push('c');执行时,没有其他活跃的可变引用指向该String,所以可以正常执行。
而取消注释ss.push('d');后报错的原因是:
let b = &mut (*ss);创建了活跃的可变引用b,此时ss的可变借用被临时冻结。ss.push('d');尝试再次借用ss的可变引用,但此时b的借用权还未释放(因为后续还有b.push('b');的使用),违反了“同一时间只能有一个可变引用指向同一数据”的核心规则,因此编译器报错。
4. 关键规则总结
- 可变引用的冲突判断基于活跃使用期,而非变量的声明作用域。
- 未被使用的引用,编译器会直接释放其借用权。
- 同一时间只能存在一个指向同一数据的活跃可变引用,前一个引用的活跃期结束后,后续可复用或创建新的可变引用。
内容的提问来源于stack exchange,提问作者杨尚山
相关产品推荐
相关产品推荐

