如何解决生存期错误以在Rust中进行可变引用? [英] How to resolve lifetime error for mutable reference in Rust?
问题描述
我不确定为什么以下代码无法编译.
I am not sure why the following code does not compile.
use std::cmp::Ordering;
struct MyItr<'a> {
cur: &'a i32,
}
impl<'a> Ord for MyItr<'a> {
fn cmp(&self, other: &MyItr) -> Ordering {
self.cur.cmp(&other.cur)
}
}
impl<'a> PartialOrd for MyItr<'a> {
fn partial_cmp(&self, other: &MyItr<'a>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> PartialEq for MyItr<'a> {
fn eq(&self, other: &MyItr) -> bool {
self.cur == other.cur
}
}
impl<'a> Eq for MyItr<'a> {}
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32) {
let t = std::cmp::max(t0, t1);
t.cur = i;
}
fn f1() {
let i0 = 1;
let i1 = 2;
let mut z0 = MyItr { cur: &i0 };
let mut z1 = MyItr { cur: &i1 };
let i2 = 3;
f0(&mut z0, &mut z1, &i2);
}
$ cargo build
Compiling foo v0.1.0 (file:///private/tmp/foo)
error: `z1` does not live long enough
--> lib.rs:40:1
|
39 | f0(&mut z0, &mut z1, &i2);
| -- borrow occurs here
40 | }
| ^ `z1` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
我的理解是,一旦f0
调用结束,就会备份对z0
和z1
的借用引用.但是,编译器似乎假定不支持借用的引用.
My understanding is the borrowed references to z0
and z1
are backed once the f0
invocation ends. However, The compiler seems to assume the borrowed references are not backed.
$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)
推荐答案
这里有两个问题.首先是您的生存期被过度指定,从而造成了编译器无法处理的情况.
There are two problems here. The first is that you've over-specified lifetimes, creating a situation the compiler just can't deal with.
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
您已经告诉编译器,所有参数都必须是具有相同寿命的指针.编译器可以缩短重叠的生存期,但是在这种情况下没有帮助.您已指定MyItr
的指针与它们指向的对象具有相同的生存期,和外部指针是可变的.
You've told the compiler that all the arguments must be pointers with the same lifetime. The compiler can narrow overlapping lifetimes, but in this case that doesn't help. You've specified that the pointer to the MyItr
s has the same lifetime as the thing they point to, and the outer pointers are mutable.
第二个问题是(即使在修复该问题之后),您尝试做的事情也是完全不安全的,并且会导致指针悬空.
The second problem is that (even after fixing that), what you're trying to do is just outright unsafe and will lead to dangling pointers.
这是更多的最小示例:
struct S<'a> {
ptr: &'a i32,
}
fn f<'b>(t: &'b mut S<'b>, new_ptr: &'b i32) {}
fn main() {
let i0 = 1;
let mut s = S { ptr: &i0 };
let i1 = 2;
f(&mut s, &i1);
}
什么是'b
?好吧,编译器只能 缩短生存期,因此通常您只需要尝试传递的所有事物的生存期,并选择最短的生命周期.在这种情况下,这就是i1
的生存期.因此,必须缩短&s
的寿命.指向s
本身的指针的生存期不是问题(您可以缩小借用的时间),但是缩小内部生存期(用于ptr
字段的生存期)是 是一个问题.
What is 'b
? Well, the compiler can only narrow lifetimes, so usually you'd just take the lifetimes of everything you're trying to pass and pick the shortest one. In this case, that would be the lifetime of i1
. So, it has to narrow the lifetime of &s
. The lifetime on the pointer to s
itself isn't a problem (you can narrow how long you take a borrow for), but narrowing the inner lifetime (the one used for the ptr
field) is a problem.
如果编译器缩小了s.ptr
的生存期,则可以将&i1
存储在该字段中. s
期望s.ptr
会生存下去,但这不再成立:i1
将在s
之前被破坏,这意味着s.ptr
将包含一个悬空指针.而且Rust 不会允许这种情况发生.
If the compiler narrowed the lifetime of s.ptr
, you would then be able to store &i1
in that field. s
expects s.ptr
to outlive itself, but that will no longer be true: i1
will be destroyed before s
is, meaning s.ptr
will contain a dangling pointer. And Rust will not allow that to happen.
因此,Rust 不能缩小s
的内部'a
生存期...但是,如果不能缩小它的寿命,则意味着'b
必须是完整,未缩小的'a
.但是,等等,这意味着'b
比s
本身和 i1
的生存期更长.那是不可能的.
As a result, Rust can't narrow s
's inner 'a
lifetime... but if it can't narrow it, then that means 'b
must be the full, un-narrowed 'a
. But wait, that means that 'b
is longer than the lifetime of s
itself and i1
. And that's impossible.
因此失败.
该解决方案需要两件事.首先,您不必过多指定生存期.其次,您需要确保某个有效生命期完全存在.对于您的原始代码,这意味着将i2
移到z0
和z1
上方,以使其寿命更长.像这样:
The solution requires two things. First, you need to not over-specify lifetimes. Secondly, you need to ensure that some valid lifetime exists at all; in the case of your original code, that means moving i2
above z0
and z1
so that it outlives them. Like so:
fn f0<'a>(t0: &mut MyItr<'a>, t1: &mut MyItr<'a>, i: &'a i32) {
let t: &mut MyItr<'a> = std::cmp::max(t0, t1);
t.cur = i;
}
fn f1() {
let i0 = 1;
let i1 = 2;
let i2 = 3;
let mut z0 = MyItr { cur: &i0 };
let mut z1 = MyItr { cur: &i1 };
f0(&mut z0, &mut z1, &i2);
}
经验法则:不要只是到处发送垃圾邮件.只能将相同的生命周期用于应该相同的事物.
A rule of thumb: don't just spam a single lifetime everywhere. Only use the same lifetime for things that should be the same.
这篇关于如何解决生存期错误以在Rust中进行可变引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!