为什么同一时刻只能有一个 &mut?除了防止数据竞争,独占性还给编译器带来了好处。这篇从四个角度验证:最常见的违规场景(迭代时修改)、合法的拆分(split_at_mut)、独占性换来的优化(汇编里少一次内存读取),以及 &mut 不能复制。
源码:
1 | fn main() {
2 | let mut v = vec![1, 2, 3];
3 | for x in &v {
4 | if *x == 2 { v.push(99); }
5 | }
6 | println!("{:?}", v);
7 | }
编译:失败
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> <实验目录>/src/u21a.rs:4:22
|
3 | for x in &v {
| --
| |
| immutable borrow occurs here
| immutable borrow later used here
4 | if *x == 2 { v.push(99); }
| ^^^^^^^^^^ mutable borrow occurs here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0502`. 源码:
1 | fn swap_halves(a: &mut [i32], b: &mut [i32]) { for (x, y) in a.iter_mut().zip(b.iter_mut()) { std::mem::swap(x, y); } }
2 | fn main() {
3 | let mut v = vec![1, 2, 3, 4];
4 | swap_halves(&mut v, &mut v); // 两个 &mut 指向同一个 Vec
5 | println!("{:?}", v);
6 | }
编译:失败
error[E0499]: cannot borrow `v` as mutable more than once at a time
--> <实验目录>/src/u21b.rs:4:25
|
4 | swap_halves(&mut v, &mut v); // 两个 &mut 指向同一个 Vec
| ----------- ------ ^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.
源码:
1 | fn swap_halves(a: &mut [i32], b: &mut [i32]) { for (x, y) in a.iter_mut().zip(b.iter_mut()) { std::mem::swap(x, y); } }
2 | fn main() {
3 | let mut v = vec![1, 2, 3, 4];
4 | let (left, right) = v.split_at_mut(2); // 把一个切片拆成两个不相交的 &mut
5 | swap_halves(left, right);
6 | println!("{:?}", v);
7 | }
编译:成功
运行(退出码 0):
[3, 4, 1, 2]with_mut(&mut, &mut) 汇编: mov w8, #1 ; str w8, [x0] ; mov w8, #2 ; str w8, [x1] ; mov w0, #1 ; ret with_ptr(*mut, *mut) 汇编: mov w8, #1 ; str w8, [x0] ; mov w8, #2 ; str w8, [x1] ; ldr w0, [x0] ; ret (用 &mut 时编译器知道 a 与 b 不会指向同一处,所以敢直接返回常量 1;用裸指针时 a、b 可能相同,必须在写 *b 之后重新读 *a)
汇编用 --emit=asm -O 生成;只是一个很小的例子,不能据此估计实际程序里的收益。
源码:
1 | fn main() {
2 | let mut x = 0;
3 | let a = &mut x;
4 | let b = a; // &mut 被移动
5 | *b += 1;
6 | *a += 1;
7 | }
编译:失败
error[E0382]: use of moved value: `a`
--> <实验目录>/src/u21e.rs:6:5
|
3 | let a = &mut x;
| - move occurs because `a` has type `&mut i32`, which does not implement the `Copy` trait
4 | let b = a; // &mut 被移动
| - value moved here
5 | *b += 1;
6 | *a += 1;
| ^^^^^^^ value used here after move
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.