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

时间:2017-05-20 02:15:50

标签: rust lifetime

我不确定为什么以下代码无法编译。

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调用结束后,对z1f0的借用引用会得到支持。但是,编译器似乎假设借用的引用不受支持。

$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)

1 个答案:

答案 0 :(得分:6)

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

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字段的生命周期)< em> 是个问题。

如果编译器缩短了s.ptr的生命周期,那么您就可以在该字段中存储&i1s期望s.ptr比自己更长久,但这将不再成立:i1将在s之前被销毁,这意味着{{1}将包含一个悬空指针。并且Rust 不会允许这种情况发生。

因此,Rust 无法缩短s.ptr内部s的生命周期......但如果它不能缩小它,那意味着'a必须是完整的,未缩小的'b。但是等等,这意味着'a'b本身 s的生命周期长。这是不可能的。

因此失败了。

解决方案需要两件事。首先,您不需要过度指定生命周期。其次,你需要确保存在一些有效的生命周期 ;对于原始代码,这意味着将i1移到i2z0之上,以使其超过它们。像这样:

z1

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