![](/img/trans.png)
[英]Why can I use a mutable variable such that its lifetime overlaps with an immutable reference, but I can't use a mutable reference in the same way?
[英]Why do I get a lifetime error when I use a mutable reference in a struct instead of an immutable reference?
此代码工作正常( Playground ):
struct F<'a> {
x: &'a i32,
}
impl<'a> F<'a> {
fn get<'b>(&'b self) -> &'a i32 {
self.x
}
}
fn main() {
let x = 3;
let y = F { x: &x };
let z = y.get();
}
但是当我将x
改为可变引用时( Playground ):
struct Foo<'a> {
x: &'a mut i32, // <-- `mut` added
}
impl<'a> Foo<'a> {
fn get(&self) -> &'a i32 {
self.x
}
}
fn main() {
let mut x = 3; // <-- `mut` added
let y = Foo { x: &mut x }; // <-- `mut` added
let z = y.get();
}
我收到此错误:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:7:9
|
7 | self.x
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'a as defined on the impl at 5:6...
--> src/main.rs:5:6
|
5 | impl<'a> Foo<'a> {
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
--> src/main.rs:6:5
|
6 | / fn get(&self) -> &'a i32 {
7 | | self.x
8 | | }
| |_____^
为什么会这样? 在我看来,生命周期没有任何改变:所有值/引用仍然与第一个代码片段中的一样长。
为什么 Rust 编译器拒绝get
这种实现? 因为它允许:
以下是一个完全合理的main
,假设get
编译:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
但是,如果要编译get
,那就没问题了:
a
不借y
因为生命周期不同b
“消耗” y
(从yx
移动)但我们不会在之后重用它所以一切都很好,除了我们现在有一个&i32
和&mut i32
都指向x
。
注意:要使其编译,您可以在get
使用unsafe
: unsafe { std::mem::transmute(&*self.x) }
; 可怕,嗯?
借用检查算法的核心是构建 Rust 内存安全的基石:
别名异或可变性
Rust 通过保证无论何时修改某些东西,都没有观察者可以在可能变成悬空的东西中拥有引用,从而在没有垃圾收集的情况下实现内存安全。
反过来,这让我们解释:
&T
作为别名参考; 它是Copy
&mut T
作为唯一引用; 它不是Copy
,因为它会违反唯一性,但它可以被移动这种差异拯救了我们。
由于&mut T
无法复制,因此从&mut T
转到&T
(或&mut T
)的唯一方法是执行重新借用:取消引用并引用结果。
这是由编译器隐式完成的。 手动执行会产生更好的错误消息:
fn get<'b>(&'b self) -> &'a i32 {
&*self.x
}
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements --> <anon>:7:9 | 7 | &*self.x | ^^^^^^^^ | help: consider using an explicit lifetime parameter as shown: fn get(&'a self) -> &'a i32 --> <anon>:6:5 | 6 | fn get<'b>(&'b self) -> &'a i32 { | ^
为什么它不能推断一生? 因为重新借用的生命周期受到'b
限制,但我们需要一个'a
并且两者之间没有关系!
顺便说一下,这就是让我们避免犯错的原因,因为它确保在结果存在时必须借用Foo
实例(防止我们通过Foo::x
使用可变引用)。
遵循编译器提示,并返回&'b i32
工作......并防止上述main
编译:
impl<'a> Foo<'a> {
fn get<'b>(&'b self) -> &'b i32 {
&*self.x
}
}
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
error[E0505]: cannot move out of `yx` because it is borrowed --> <anon>:16:9 | 15 | let a = y.get(); | - borrow of `y` occurs here 16 | let b = yx; | ^ move out of `yx` occurs here
但是它可以让第一个main
编译没有问题:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let z = y.get();
println!("{}", z);
}
打印3
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.