如何解决Rust中可变引用的生命周期错误?

rui*_*imo 2 lifetime 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);
}
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

我的理解是借用的引用,z0z1f0调用结束后得到支持.但是,编译器似乎假设借用的引用不受支持.

$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)
Run Code Online (Sandbox Code Playgroud)

DK.*_*DK. 6

这里有两个问题.首先是你过度指定了生命周期,创造了编译器无法处理的情况.

fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
Run Code Online (Sandbox Code Playgroud)

您告诉编译器所有参数必须是具有相同生命周期的指针.编译器可以缩短重叠的生命周期,但在这种情况下,这无济于事.你指定的指针MyItrS具有相同的寿命,因为它们指向的东西,外指针是可变的.

第二个问题是(即使在确定之后),你要做的只是完全不安全并且会导致悬空指针.

这是一个小的例子:

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);
}
Run Code Online (Sandbox Code Playgroud)

什么是'b?好吧,编译器只能缩短生命周期,所以通常你只需要考虑你想要传递的所有东西的生命周期并选择最短的一个.在这种情况下,这将是生命的i1.所以,它必须缩短寿命&s.指向s自身的指针的生命周期不是问题(您可以缩短借用的时间),但缩小内部生命周期(用于ptr字段的生命周期)一个问题.

如果编译器缩短了生命周期s.ptr,那么您就可以存储&i1在该字段中. s期望s.ptr自己超过自己,但这将不再是真的:i1被摧毁之前s,意味着s.ptr将包含一个悬垂的指针.而Rust 也不会允许这种情况发生.

因此,鲁斯特不能缩小s内心的'a生命周期...但如果它不能缩小它,那么这意味着'b必须是完整的,不缩小的'a.别急,这意味着'b长于寿命s本身 i1.这是不可能的.

因此失败了.

解决方案需要两件事.首先,您不需要过度指定生命周期.其次,你需要确保一些有效寿命存在于所有 ; 在原始代码的情况下,这意味着移动到i2上面z0z1使它超过它们.像这样:

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);
}
Run Code Online (Sandbox Code Playgroud)

经验法则:不要只是在任何地方发送垃圾邮件.对于应该相同的事物,只能使用相同的生命周期.