Rust中的错误与特征对象的生命周期混淆

时间:2016-09-06 01:28:28

标签: rust lifetime trait-objects

任何人都可以通过以下代码说出问题所在吗?编译器抱怨生命周期,但错误消息绝对没有意义。我已经尝试了所有我能想到的东西,但似乎没有任何帮助。

use std::borrow::BorrowMut;

trait Trait<'a> {
    fn accept(&mut self, &'a u8);
}

struct Impl<'a>{
    myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
    fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl{myref: None})
}

fn user<'a>(obj: &mut Trait<'a>) {}

fn parent<'a>(x: &'a u8) {
    let mut pool = new();
    user(pool.borrow_mut());
}

编译器错误是

error: `pool` does not live long enough
  --> src/wtf.rs:22:10
   |
22 |     user(pool.borrow_mut());
   |          ^^^^ does not live long enough
23 | }
   | - borrowed value dropped before borrower
   |
   = note: values in a scope are dropped in the opposite order they are created

这绝对没有意义。借款人的生活如何?我甚至没有使用借来的价值!

3 个答案:

答案 0 :(得分:10)

好的,这个确实有意义,但由于终身省略而很难看到。所以,这里的代码是所有明确写出的生命周期,并且无关的细节被剔除:

use std::borrow::BorrowMut;

trait Trait<'a> {}

struct Impl<'a> {
    myref: Option<&'a u8>,
}

impl<'a> Trait<'a> for Impl<'a> {}

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl { myref: None })
}

fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}

fn parent() {
/* 'i: */   let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */   let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
            /* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */   let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
            user(pool_borrow);
}

现在,从parent的最后一行的角度来看,我们可以通过阅读user的定义并替换parent中的生命周期来计算出以下等价:

  • 'a = 'x
  • 'b = 'i
  • 'b = 'x

此外,这让我们得出结论:

  • 'x = 'i

这就是问题所在。由于您已定义user的方式,您已将自己置于pool_ref借用的生命周期(等于{{1}的生命周期)的情况中您要借用的存储位置必须与pool存储中使用的生存期'x相同。

有点像pool能够在它存在之前拥有指向自身的指针,这没有任何意义。

无论哪种方式,修复都很简单。将Box更改为实际具有正确的类型:

user

这匹配fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {} 生成的类型。或者,不要使用new

borrow_mut

有效,因为它是&#34;重新借用&#34;。调用user(&mut *pool) 或多或少地直接转换生命周期,但重新借用允许编译器将借用缩小到更短的生命周期。换句话说,明确地调用borrow_mut并不能让编译器有足够的自由去软化&#34;使他们全部排队的生命周期,重新借用

快速搁置:

  

我甚至没有使用借来的价值!

不相关。 Rust在本地完成完全的类型和生命周期检查。 永远不会查看另一个函数的主体,看看它在做什么;它仅在界面上进行。编译器既不检查也不关心你在里面做一个不同的函数。

答案 1 :(得分:4)

请注意,错误消息还有更多内容:

error: `pool` does not live long enough
  --> src/main.rs:25:10
   |>
25 |>     user(pool.borrow_mut());
   |>          ^^^^
note: reference must be valid for the block at 23:25...
  --> src/main.rs:23:26
   |>
23 |> fn parent<'a>(x: &'a u8) {
   |>                          ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
  --> src/main.rs:24:26
   |>
24 |>     let mut pool = new();
   |>                          ^

让我们看一下user

fn user<'a>(obj: &mut Trait<'a>) {}

这表示它将接受一个带有生命期'a参数化的特征对象的可变引用(具有未命名的生命周期)。

转向new,我会说这种方法高度可疑:

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl { myref: None })
}

这表示它将返回一个盒装特征对象,其中调用者指定的任何生命周期That basically never makes sense

所有这一切,我不清楚为什么代码选择使用borrow_mut。我会更直接地写出来:

user(&mut *pool);

取消引用Box<Trait>获取Trait,然后获取可变参考,产生&mut Trait,编译。

我目前无法解释为什么BorrowMut的行为不同。

答案 2 :(得分:2)

我不确定为什么会发生此错误,但我可以提供解决方案!

首先,似乎使用borrow_mut会不必要地限制返回引用的生命周期。使用运算符创建引用可以解决错误。

fn parent() {
    let mut pool = new();
    user(&mut *pool);
}

但是,如果我们这样做,我们可以通过在Trait&#39;中添加user对象的生命周期来解决错误。 s obj参数。

fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}