如何解决 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

我的理解是 z0z1 的借用引用在 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 的生命周期,您就可以在该字段中存储 &i1s 期望 s.ptr 比自己长寿,但这将不再是真的:i1s 之前被销毁 ,这意味着s.ptr 将包含一个悬挂指针。而 Rust 不会 允许这种情况发生。

因此,Rust 不能 缩小 s 的内部 'a 生命周期......但如果它不能缩小它,那么这意味着 'b 必须是完整的、未缩小的 'a。但是等等,这意味着 'bs 本身 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);
}

一条经验法则:不要到处发送垃圾邮件。只对应该相同的事物使用相同的生命周期。