如何解决生存期错误以在Rust中进行可变引用? [英] How to resolve lifetime error for mutable reference in Rust?

查看:78
本文介绍了如何解决生存期错误以在Rust中进行可变引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am not sure why the following code does not compile.

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

我的理解是,一旦f0调用结束,就会备份对z0z1的借用引用.但是,编译器似乎假定不支持借用的引用.

My understanding is the borrowed references to z0 and z1 are backed once the f0 invocation ends. However, The compiler seems to assume the borrowed references are not backed.

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

推荐答案

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

There are two problems here. The first is that you've over-specified lifetimes, creating a situation the compiler just can't deal with.

fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)

您已经告诉编译器,所有参数都必须是具有相同寿命的指针.编译器可以缩短重叠的生存期,但是在这种情况下没有帮助.您已指定MyItr的指针与它们指向的对象具有相同的生存期,外部指针是可变的.

You've told the compiler that all the arguments must be pointers with the same lifetime. The compiler can narrow overlapping lifetimes, but in this case that doesn't help. You've specified that the pointer to the MyItrs has the same lifetime as the thing they point to, and the outer pointers are mutable.

第二个问题是(即使在修复该问题之后),您尝试做的事情也是完全不安全的,并且会导致指针悬空.

The second problem is that (even after fixing that), what you're trying to do is just outright unsafe and will lead to dangling pointers.

这是更多的最小示例:

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字段的生存期)是

What is 'b? Well, the compiler can only narrow lifetimes, so usually you'd just take the lifetimes of everything you're trying to pass and pick the shortest one. In this case, that would be the lifetime of i1. So, it has to narrow the lifetime of &s. The lifetime on the pointer to s itself isn't a problem (you can narrow how long you take a borrow for), but narrowing the inner lifetime (the one used for the ptr field) is a problem.

如果编译器缩小了s.ptr的生存期,则可以将&i1存储在该字段中. s期望s.ptr会生存下去,但这不再成立:i1将在s之前被破坏,这意味着s.ptr将包含一个悬空指针.而且Rust 不会允许这种情况发生.

If the compiler narrowed the lifetime of s.ptr, you would then be able to store &i1 in that field. s expects s.ptr to outlive itself, but that will no longer be true: i1 will be destroyed before s is, meaning s.ptr will contain a dangling pointer. And Rust will not allow that to happen.

因此,Rust 不能缩小s的内部'a生存期...但是,如果不能缩小它的寿命,则意味着'b必须是完整,未缩小的'a.但是,等等,这意味着'bs本身 i1的生存期更长.那是不可能的.

As a result, Rust can't narrow s's inner 'a lifetime... but if it can't narrow it, then that means 'b must be the full, un-narrowed 'a. But wait, that means that 'b is longer than the lifetime of s itself and i1. And that's impossible.

因此失败.

该解决方案需要两件事.首先,您不必过多指定生存期.其次,您需要确保某个有效生命期完全存在.对于您的原始代码,这意味着将i2移到z0z1上方,以使其寿命更长.像这样:

The solution requires two things. First, you need to not over-specify lifetimes. Secondly, you need to ensure that some valid lifetime exists at all; in the case of your original code, that means moving i2 above z0 and z1 so that it outlives them. Like so:

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);
}

经验法则:不要只是到处发送垃圾邮件.只能将相同的生命周期用于应该相同的事物.

A rule of thumb: don't just spam a single lifetime everywhere. Only use the same lifetime for things that should be the same.

这篇关于如何解决生存期错误以在Rust中进行可变引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆