借用检查
Rust’s borrow checker puts constraints on the ways you can borrow values. We’ve already seen that a reference cannot outlive the value it borrows:
fn main() { let x_ref = { let x = 10; &x }; dbg!(x_ref); }
There’s also a second main rule that the borrow checker enforces: The aliasing rule. For a given value, at any time:
- You can have one or more shared references to the value, or
- You can have exactly one exclusive reference to the value.
fn main() { let mut a = 10; let b = &a; { let c = &mut a; *c = 20; } dbg!(a); dbg!(b); }
This slide should take about 10 minutes.
- The “outlives” rule was demonstrated previously when we first looked at references. We review it here to show students that the borrow checking is following a few different rules to validate borrowing.
- 上述代码无法编译,因为
a
同时作为可变值(通过c
)和不可变值(通过b
)被借用。- Note that the requirement is that conflicting references not exist at the same point. It does not matter where the reference is dereferenced. Try commenting out
*c = 20
and show that the compiler error still occurs even if we never usec
. - Note that the intermediate reference
c
isn’t necessary to trigger a borrow conflict. Replacec
with a direct mutation ofa
and demonstrate that this produces a similar error. This is because direct mutation of a value effectively creates a temporary mutable reference.
- Note that the requirement is that conflicting references not exist at the same point. It does not matter where the reference is dereferenced. Try commenting out
- Move the
dbg!
statement forb
before the scope that introducesc
to make the code compile.- 这样更改后,编译器会发现
b
只在通过c
对a
进行新可变借用之前使用过。这是借用检查器的一个功能,名为“非词法作用域生命周期”。
- 这样更改后,编译器会发现
探索更多
- Technically multiple mutable references to a piece of data can exist at the same time via re-borrowing. This is what allows you to pass a mutable reference into a function without invaliding the original reference. This playground example demonstrates that behavior.
- Rust uses the exclusive reference constraint to ensure that data races do not occur in multi-threaded code, since only one thread can have mutable access to a piece of data at a time.
- Rust also uses this constraint to optimize code. For example, a value behind a shared reference can be safely cached in a register for the lifetime of that reference.
- Fields of a struct can be borrowed independently of each other, but calling a method on a struct will borrow the whole struct, potentially invalidating references to individual fields. See this playground snippet for an example of this.