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

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)

最佳答案

这里有两个问题。首先是您指定了生存期,从而造成了编译器无法处理的情况。

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。但是,等等,这意味着'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);
}

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

关于rust - 如何解决生存期错误以在Rust中进行可变引用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44081272/

10-09 16:29