不可变借用与可变借用是所有权之后的第二块基石。这篇用真实的编译器报错把规则列清楚:能写什么、什么时候冲突、借用到哪里结束,以及一个容易被忽略的细节 —— &mut 传给函数时的重借用。
源码:
1 | fn add_one(n: &mut i32) { *n += 1; }
2 | fn read(n: &i32) -> i32 { *n * 2 }
3 | fn main() {
4 | let mut x = 10;
5 | println!("read(&x) = {}", read(&x));
6 | add_one(&mut x);
7 | println!("add_one 之后 x = {x}");
8 | }
编译:成功
运行(退出码 0):
read(&x) = 20
add_one 之后 x = 11
源码:
1 | fn bad(n: &i32) { *n += 1; }
2 | fn main() { let mut x = 1; bad(&x); }
编译:失败
error[E0594]: cannot assign to `*n`, which is behind a `&` reference
--> <实验目录>/src/u19b.rs:1:19
|
1 | fn bad(n: &i32) { *n += 1; }
| ^^^^^^^ `n` is a `&` reference, so it cannot be written to
|
help: consider changing this to be a mutable reference
|
1 | fn bad(n: &mut i32) { *n += 1; }
| +++
warning: variable does not need to be mutable
--> <实验目录>/src/u19b.rs:2:17
|
2 | fn main() { let mut x = 1; bad(&x); }
| ----^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default
error: aborting due to 1 previous error; 1 warning emitted
…(共 24 行) 源码:
1 | fn main() {
2 | let mut s = String::from("hi");
3 | let a = &s;
4 | let b = &mut s;
5 | b.push('!');
6 | println!("{a} {b}");
7 | }
编译:失败
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> <实验目录>/src/u19c.rs:4:13
|
3 | let a = &s;
| -- immutable borrow occurs here
4 | let b = &mut s;
| ^^^^^^ mutable borrow occurs here
5 | b.push('!');
6 | println!("{a} {b}");
| - immutable borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0502`.
源码:
1 | fn main() {
2 | let mut s = String::from("hi");
3 | let a = &mut s;
4 | let b = &mut s;
5 | a.push('1'); b.push('2');
6 | println!("{s}");
7 | }
编译:失败
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> <实验目录>/src/u19d.rs:4:13
|
3 | let a = &mut s;
| ------ first mutable borrow occurs here
4 | let b = &mut s;
| ^^^^^^ second mutable borrow occurs here
5 | a.push('1'); b.push('2');
| - first borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.规则为什么成立:共享多个读者安全(见多个不可变借用为什么可以共存),可变借用必须独占(见可变借用为什么不能同时存在)。
源码:
1 | fn main() {
2 | let mut s = String::from("hi");
3 | let a = &s;
4 | println!("a = {a}"); // a 的最后一次使用
5 | let b = &mut s; // 此刻 a 已经不再使用,所以允许
6 | b.push('!');
7 | println!("b = {b}");
8 | }
编译:成功
运行(退出码 0):
a = hi
b = hi!编译器按引用的最后一次使用来计算借用范围,见NLL 非词法生命周期。
源码:
1 | fn inc(n: &mut i32) { *n += 1; }
2 | fn main() {
3 | let mut x = 0;
4 | let r = &mut x;
5 | inc(r); // 隐式重借用:r 之后仍然可用
6 | inc(r);
7 | *r += 10;
8 | println!("x = {}", x);
9 | }
编译:成功
运行(退出码 0):
x = 12
源码:
1 | fn consume(r: &mut i32) { *r += 1; }
2 | fn main() {
3 | let mut x = 0;
4 | let r = &mut x;
5 | let moved = r; // 这里是移动,不是重借用
6 | consume(moved);
7 | consume(r);
8 | }
编译:失败
error[E0382]: borrow of moved value: `r`
--> <实验目录>/src/u19g.rs:7:13
|
4 | let r = &mut x;
| - move occurs because `r` has type `&mut i32`, which does not implement the `Copy` trait
5 | let moved = r; // 这里是移动,不是重借用
| - value moved here
6 | consume(moved);
7 | consume(r);
| ^ value borrowed here after move
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.