Rust中用自身引用遮蔽变量为何合法?新手存疑求解
Rust变量遮蔽、作用域与引用的常见误区解答
核心结论:遮蔽不终止原变量的作用域
你的代码能正常编译,核心原因是:变量遮蔽只是隐藏了原变量的名字,并没有让原变量离开作用域,因此原String不会被drop,引用自然不会悬空。
Rust中变量的销毁时机是其所在作用域结束时,而非被遮蔽时。原let s1 = String::from("hello");创建的字符串,生命周期覆盖整个main函数,直到函数结束才会被销毁。第二个let s1 = &s1;只是用同名变量覆盖了原来的名字,原字符串依然存在,所以引用是有效的。
关于你的几个具体问题
1. 能否恢复原字符串?
不能直接恢复。一旦变量被同名遮蔽,原变量的名字在当前作用域内就无法再被访问。但如果把遮蔽的变量放在子作用域中,当子作用域结束后,遮蔽失效,原变量的名字会重新可用:
fn main() { let s1 = String::from("hello"); { // 子作用域内遮蔽原s1 let s1 = &s1; println!("子作用域内的s1:{s1}"); } // 子作用域结束,原s1重新可访问 println!("原字符串:{s1}"); }
2. 能否创建可变引用?
这取决于原变量的可变性以及借用规则:
- 如果原变量是不可变的(
let s1),即使没被遮蔽,也无法创建可变引用,这是Rust的可变性规则决定的。 - 如果原变量是可变的(
let mut s1),需要注意借用规则:如果当前存在对原变量的有效引用(比如遮蔽后的s1 = &s1),则无法同时创建可变引用。但如果把遮蔽的引用放在子作用域中,子作用域结束后,引用失效,就可以对原变量创建可变引用:
fn main() { let mut s1 = String::from("hello"); { let s1 = &s1; println!("不可变引用:{s1}"); } // 子作用域结束,引用失效,可创建可变引用 s1.push_str(" world"); println!("修改后的字符串:{s1}"); }
你存在的认知误区
- 误区1:混淆遮蔽与作用域结束:遮蔽只是名字的覆盖,不是让原变量离开作用域。作用域是代码块(比如
{}包裹的区域或函数)决定的,只有当变量离开所在的作用域时,才会被销毁。 - 误区2:认为被遮蔽的变量会立即销毁:Rust采用作用域结束时销毁的规则,被遮蔽的变量只要还在作用域内,就会持续存在,直到作用域结束才会被
drop。 - 误区3:忽略借用规则与遮蔽的独立性:遮蔽不改变原变量的借用状态,即使原变量被遮蔽,只要存在对它的有效引用,依然要遵守Rust的借用规则(比如同一时间不能同时存在可变引用和不可变引用)。
内容的提问来源于stack exchange,提问作者Emily Penstemmon
相关产品推荐
相关产品推荐

