我不明白为什么借用检查器允许可变变量的生存期与不可变引用的生存期重叠,但不允许可变引用和不可变引用重叠。
它编译:
let mut s = String::from("hello"); // some warning about s not needing to be mutable
let r = &s;
println!("{}, {}", r, s);
但这不是:
let mut s = String::from("hello");
let r_mut = &mut s; // mutable borrow here
let r = &s; // immutable borrow here
println!("{}, and {}", r, r_mut); // mutable borrow used here, error
为什么我可以以不可变的方式使用可变变量,使其生存期与不可变引用重叠,但我不能以相同的不可变方式使用可变引用?
让我们先稍微更正一下术语。
变量绑定没有生存期。它们具有范围。当变量绑定超出范围时,它绑定到的对象将被丢弃。这种行为不受借款检查器的控制。作用域是词法的,在查看源代码时很容易看到。
借款是有生命的。这些生存期将由借用检查器推断,并且它们与代码的词汇结构("非词汇生存期"(无关,尽管它们已经在过去了。使用寿命大致从创建借用的点延伸到最后使用借用的点。
创建变量借用时,借用检查器会将该变量标记为借用。借用检查器推断,在借用的生存期期间,无论您是否将变量绑定声明为可变,都不能更改或移动借用的变量。如果您创建了共享借用,则可以创建更多的共享借用。然而,可变借用是排他性的——可变借用的变量在借用的生命周期内不能以任何其他方式使用。
这些规则确保变量的一个"句柄"——绑定或可变借位——在任何给定时间都可以用于变异变量。这是Rust确保的基本不变量。可变变量绑定的范围可以与借位的生存期重叠,这一事实并不能改变这一点,因为在借位的生命期内,您不能通过其绑定来修改变量。
严格地说,不存在所谓的"可变变量";只有可变变量绑定。如果你拥有一个对象的所有权,你总是可以可变地绑定到它来修改它:
let s = "Hello".to_owned();
let mut s = s;
s.push_str(", world!");
代码还有一个微妙之处:println!()
是一个宏,而不是函数调用。它扩展到一些仅借用传递给println!()
的参数的代码。因此,虽然看起来您正在将s
的所有权传递给println!()
,但实际上并没有。如果您使用一个拥有所有权的宏,代码将停止编译:
let mut s = String::from("hello");
let r = &s;
dbg!(r, s);
导致错误
error[E0505]: cannot move out of `s` because it is borrowed
--> src/main.rs:4:5
|
3 | let r = &s;
| -- borrow of `s` occurs here
4 | dbg!(r, s);
| ^^^^^^^^^^^
| |
| move out of `s` occurs here
| borrow later used here
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
另请参阅:
- 打印!借用还是拥有变量