7

谁能告诉下面的代码有什么问题?编译器抱怨生命周期,但错误消息绝对没有意义。我已经尝试了我能想到的一切,但似乎没有任何帮助。

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

这完全没有意义。借款人的寿命如何?我什至没有使用借来的价值!

4

3 回答 3

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(等于pool您从中借用的存储位置的生命周期)必须与'x使用的生命周期相同的情况下存储的东西pool

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

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

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

这与 生成的类型相匹配new。或者,只是不要使用borrow_mut

user(&mut *pool)

是有效的,因为它是“重新借用”。调用borrow_mut或多或少直接转换生命周期,但重新借用允许编译器将借用范围缩小到更短的生命周期。换句话说,显式调用borrow_mut不允许编译器有足够的自由来“捏造”生命周期以使它们全部对齐,重新借用可以

顺便说一句:

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

无关紧要。 Rust完全在本地进行类型和生命周期检查。它从不查看另一个函数的主体来查看它在做什么;它单独出现在界面上。编译器既不检查也不关心你在不同的函数中所做的事情。

于 2016-09-06T03:30:02.070 回答
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 })
}

这表示它将返回一个装箱的 trait 对象,其生命周期是调用者指定的这基本上没有任何意义

说了这么多,我不清楚为什么代码选择使用borrow_mut. 我会更直接地写:

user(&mut *pool);

这会取消引用Box<Trait>以获取 a Trait,然后采用可变引用 yielding &mut Trait,然后编译。

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

于 2016-09-06T01:48:35.290 回答
2

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

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

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

但是,如果我们这样做,我们可以通过在的参数中添加绑定到Trait对象的生命周期来解决错误。userobj

fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}
于 2016-09-06T01:52:35.730 回答