如何解决 Rust 中可变引用的生命周期错误?
How to resolve lifetime error for mutable reference in Rust?
我不确定为什么以下代码无法编译。
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
我的理解是 z0
和 z1
的借用引用在 f0
调用结束后得到支持。但是,编译器似乎假设借用的引用没有得到支持。
$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)
这里有两个问题。首先是您过度指定了生命周期,造成编译器无法处理的情况。
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
您已经告诉编译器所有参数必须是具有相同生命周期的指针。编译器可以缩小重叠的生命周期,但在这种情况下没有帮助。您已指定指向 MyItr
的指针与它们指向的对象具有相同的生命周期, 和 外部指针是可变的。
第二个问题是(即使在修复该问题之后),您尝试做的事情是完全不安全的,并且会导致悬空指针。
这是一个 更多 最小示例:
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
字段的生命周期)是一个问题。
如果编译器缩短 s.ptr
的生命周期,您就可以在该字段中存储 &i1
。 s
期望 s.ptr
比自己长寿,但这将不再是真的:i1
将 在 s
之前被销毁 ,这意味着s.ptr
将包含一个悬挂指针。而 Rust 不会 允许这种情况发生。
因此,Rust 不能 缩小 s
的内部 'a
生命周期......但如果它不能缩小它,那么这意味着 'b
必须是完整的、未缩小的 'a
。但是等等,这意味着 'b
比 s
本身 和 i1
的生命周期更长。那是不可能的。
因此失败。
解决方案需要两件事。首先,您不需要过度指定生命周期。其次,你需要确保存在一些有效的生命周期完全;对于您的原始代码,这意味着将 i2
移动到 z0
和 z1
之上,使其比它们更长。像这样:
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);
}
一条经验法则:不要到处发送垃圾邮件。只对应该相同的事物使用相同的生命周期。
我不确定为什么以下代码无法编译。
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
我的理解是 z0
和 z1
的借用引用在 f0
调用结束后得到支持。但是,编译器似乎假设借用的引用没有得到支持。
$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)
这里有两个问题。首先是您过度指定了生命周期,造成编译器无法处理的情况。
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
您已经告诉编译器所有参数必须是具有相同生命周期的指针。编译器可以缩小重叠的生命周期,但在这种情况下没有帮助。您已指定指向 MyItr
的指针与它们指向的对象具有相同的生命周期, 和 外部指针是可变的。
第二个问题是(即使在修复该问题之后),您尝试做的事情是完全不安全的,并且会导致悬空指针。
这是一个 更多 最小示例:
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
字段的生命周期)是一个问题。
如果编译器缩短 s.ptr
的生命周期,您就可以在该字段中存储 &i1
。 s
期望 s.ptr
比自己长寿,但这将不再是真的:i1
将 在 s
之前被销毁 ,这意味着s.ptr
将包含一个悬挂指针。而 Rust 不会 允许这种情况发生。
因此,Rust 不能 缩小 s
的内部 'a
生命周期......但如果它不能缩小它,那么这意味着 'b
必须是完整的、未缩小的 'a
。但是等等,这意味着 'b
比 s
本身 和 i1
的生命周期更长。那是不可能的。
因此失败。
解决方案需要两件事。首先,您不需要过度指定生命周期。其次,你需要确保存在一些有效的生命周期完全;对于您的原始代码,这意味着将 i2
移动到 z0
和 z1
之上,使其比它们更长。像这样:
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);
}
一条经验法则:不要到处发送垃圾邮件。只对应该相同的事物使用相同的生命周期。