「变量默认不可变」是 Rust 最先遇到的规则。这篇把规则逐条变成编译器的原话:哪些写法会报什么错、错误提示怎么指出两次赋值的位置、延迟初始化到底允许什么,最后用汇编确认 mut 这个修饰词在运行时一点代价都没有。
源码:
1 | fn main() {
2 | let x = 5;
3 | println!("x = {x}");
4 | x = 6;
5 | println!("x = {x}");
6 | }
编译:失败
error[E0384]: cannot assign twice to immutable variable `x`
--> <实验目录>/src/u3a.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("x = {x}");
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut x = 5;
| +++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0384`.注意编译器给的修复建议(consider making this binding mutable)—— 但是否该这么改,要看你为什么要重新赋值。很多时候新建一个变量(见Shadowing 与 mut 的区别)更合适。
源码:
1 | fn main() {
2 | let mut x = 5;
3 | println!("x = {x}");
4 | x = 6;
5 | println!("x = {x}");
6 | let y = x; // 把 mut 变量的值赋给不可变变量:允许
7 | println!("y = {y}");
8 | }
编译:成功
运行(退出码 0):
x = 5
x = 6
y = 6 源码:
1 | struct P { a: i32 }
2 | fn main() {
3 | let p = P { a: 1 };
4 | p.a = 2;
5 | let v = Vec::new();
6 | v.push(1);
7 | println!("{} {}", p.a, v.len());
8 | }
编译:失败
error[E0594]: cannot assign to `p.a`, as `p` is not declared as mutable
--> <实验目录>/src/u3c.rs:4:5
|
4 | p.a = 2;
| ^^^^^^^ cannot assign
|
help: consider changing this to be mutable
|
3 | let mut p = P { a: 1 };
| +++
error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable
--> <实验目录>/src/u3c.rs:6:5
|
6 | v.push(1);
| ^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
5 | let mut v = Vec::new();
| +++
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0594, E0596.
For more information about an error, try `rustc --explain E0594`.字段赋值与 push 为什么要求 mut,与借用规则有关:push 需要 &mut self,而要拿到可变借用,绑定必须是 mut 的(见不可变借用与可变借用)。
源码:
1 | fn main() {
2 | let x;
3 | let flag = std::env::args().count() > 1;
4 | if flag { x = 1; } else { x = 2; }
5 | println!("x = {x}");
6 | }
编译:成功
运行(退出码 0):
x = 2
源码:
1 | fn main() {
2 | let x;
3 | x = 1;
4 | x = 2;
5 | println!("x = {x}");
6 | }
编译:失败
error[E0384]: cannot assign twice to immutable variable `x`
--> <实验目录>/src/u3e.rs:4:5
|
3 | x = 1;
| ----- first assignment to `x`
4 | x = 2;
| ^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut x;
| +++
warning: value assigned to `x` is never read
--> <实验目录>/src/u3e.rs:3:5
|
3 | x = 1;
| ^^^^^ this value is reassigned later and never used
4 | x = 2;
| ----- `x` is overwritten here before the previous value is read
|
= note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
error: aborting due to 1 previous error; 1 warning emitted
…(共 26 行)imm 的汇编: mov w8, #6 ; mov w9, #2 ; madd w0, w0, w8, w9 ; ret mutv 的汇编: (没有单独的函数体:汇编里是一行 `_mutv = _imm`,LLVM 发现两个函数完全相同,把 mutv 合并成了 imm 的别名) 两个函数的机器码相同: True (Rust 的可变性检查全在编译期的借用检查阶段,生成的机器码里没有 mut 的痕迹)
这是「零成本抽象」的一个最小例子(见Zero-Cost Abstraction)。