当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误? [英] Why do I get a lifetime error when I use a mutable reference in a struct instead of an immutable reference?
问题描述
此代码可以正常工作(游乐场):
This code works fine (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();
}
But when I change x
to be a mutable reference instead (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 | | }
| |_____^
为什么会这样?据我所知,生存期没有任何变化:所有值/引用的生存时间与第一个代码段中的生存时间完全一样.
Why does that happen? As far as I see it, nothing about the lifetimes has changed: all values/references still live exactly as long as in the first code snippet.
推荐答案
为什么Rust编译器拒绝get
的这种实现?因为它允许:
Why does the Rust compiler reject this implementation of get
? Because it allows:
以下是完全合理的main
,假设get
可以编译:
The following is a perfectly reasonable main
, assuming that get
compiles:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
但是,如果要编译get
,就可以了:
Yet if get
were to compile, this would be fine:
-
a
不借用y
,因为生命周期不同 -
b
消费"y
(从y.x
移出),但之后我们不再重用
a
does not borrowy
because the lifetime is differentb
"consumes"y
(moving fromy.x
) but we do not reuse it after
所以一切都很好,只不过我们现在有一个&i32
和&mut i32
两者都指向x
.
So everything is fine, except that we now have a &i32
and &mut i32
both pointing to x
.
注意:要进行编译,可以在get
中使用unsafe
:unsafe { std::mem::transmute(&*self.x) }
;吓人吧?
Note: to make it compile, you can use unsafe
inside of get
: unsafe { std::mem::transmute(&*self.x) }
; scary, eh?
借入检查算法的核心是构建Rust的内存安全性的基石:
At the heart of the borrow-checking algorithm is the cornerstone on which Rust's memory safety is built:
别名异或性
Rust通过保证在您进行任何修改时,没有观察者在其中可能存在悬挂对象的引用,从而确保了内存的安全而不进行垃圾收集.
Rust achieves memory safety without garbage collection by guaranteeing that whenever you are modifying something, no observer can have a reference inside that something that could become dangling.
这反过来让我们解释:
-
&T
作为别名参考;是Copy
-
&mut T
作为唯一参考;它不是Copy
,因为它会违反唯一性,但可以移动
&T
as an aliasing reference; it isCopy
&mut T
as a unique reference; it is NOTCopy
, as it would violate uniqueness, but it can be moved
这种差异在这里救了我们
This difference saved us here.
由于不能复制&mut T
,因此从&mut T
到&T
(或&mut T
)的唯一方法是执行重新借用:取消引用并引用结果.
Since &mut T
cannot be copied, the only way to go from &mut T
to &T
(or &mut T
) is to perform a re-borrowing: dereference and take a reference to the result.
这是由编译器隐式完成的.手动执行此操作会产生更好的错误消息:
This is done implicitly by the compiler. Doing it manually makes for a somewhat better error message:
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
,所以两者之间没有任何关系!
Why cannot it infer a lifetime? Because lifetime of the re-borrow is limited by 'b
but we are requiring a 'a
and there's no relationship between the two!
顺便说一下,这就是使我们免于犯错误的原因,因为它确保了在结果存在时必须借用实例Foo
(防止我们通过<使用可变引用c28>).
By the way, this is what is saving us from blunder here, because it ensures that the instance Foo
must be borrowed while the result lives (preventing us to use a mutable reference via Foo::x
).
遵循编译器提示,然后返回&'b i32
可以正常工作...并阻止上述main
编译:
Following the compiler hint, and returning &'b i32
works... and prevents the above main
from compiling:
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 `y.x` because it is borrowed
--> <anon>:16:9
|
15 | let a = y.get();
| - borrow of `y` occurs here
16 | let b = y.x;
| ^ move out of `y.x` occurs here
但是,它可以使第一个main
编译没有问题:
However it lets the first main
compile without issue:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let z = y.get();
println!("{}", z);
}
打印3
.
这篇关于当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!