再写一次 let 和写 let mut 看起来都是「改一个变量」,实际差别很大。这篇用真实的编译与运行结果比较两者:能不能换类型、作用域、是不是同一个变量,以及一个常被忽略的点 —— 被遮蔽的值什么时候释放。
源码:
1 | fn main() {
2 | let spaces = " ";
3 | let spaces = spaces.len(); // &str → usize:新变量,新类型
4 | println!("spaces = {spaces}");
5 | }
编译:成功
运行(退出码 0):
spaces = 3
源码:
1 | fn main() {
2 | let mut spaces = " ";
3 | spaces = spaces.len(); // 想用 mut 把 &str 变成 usize
4 | println!("spaces = {spaces}");
5 | }
编译:失败
error[E0308]: mismatched types
--> <实验目录>/src/u4b.rs:3:14
|
2 | let mut spaces = " ";
| ----- expected due to this value
3 | spaces = spaces.len(); // 想用 mut 把 &str 变成 usize
| ^^^^^^^^^^^^ expected `&str`, found `usize`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.shadowing 的典型用途就是「把解析前的字符串变成解析后的数」而不必起两个名字(见Option 与 Result里的 parse)。
源码:
1 | fn main() {
2 | let x = 5;
3 | let x = x + 1;
4 | {
5 | let x = x * 2;
6 | println!("内层 x = {x}");
7 | }
8 | println!("外层 x = {x}");
9 | }
编译:成功
运行(退出码 0):
内层 x = 12
外层 x = 6 源码:
1 | fn main() {
2 | let x = 1u64;
3 | let p1 = &x as *const u64 as usize;
4 | let x = x + 1;
5 | let p2 = &x as *const u64 as usize;
6 | println!("shadowing:两次 x 的地址相同吗? {}", p1 == p2);
7 | let mut y = 1u64;
8 | let q1 = &y as *const u64 as usize;
9 | y += 1;
10 | let q2 = &y as *const u64 as usize;
11 | println!("mut:两次 y 的地址相同吗? {}", q1 == q2);
12 | }
编译:成功
运行(退出码 0):
shadowing:两次 x 的地址相同吗? false
mut:两次 y 的地址相同吗? true地址只是一个旁证,不是语言保证。
源码:
1 | struct D(&'static str);
2 | impl Drop for D { fn drop(&mut self) { println!("drop {}", self.0); } }
3 | fn main() {
4 | let a = D("第一个");
5 | println!("创建第一个");
6 | let a = D("第二个");
7 | println!("遮蔽之后,第一个还在吗?(还没有 drop 它)");
8 | let _ = &a;
9 | println!("main 结束");
10 | }
编译:成功
运行(退出码 0):
创建第一个
遮蔽之后,第一个还在吗?(还没有 drop 它)
main 结束
drop 第二个
drop 第一个Drop 的顺序规则见Drop Trait 与资源释放。
源码:
1 | fn main() {
2 | let s = String::from("hello");
3 | let s = s + ", world"; // 旧 s 被移动进 + ,新 s 是结果
4 | println!("{s}");
5 | }
编译:成功
运行(退出码 0):
hello, world