简体   繁体   English

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

[英]Confusing error in Rust with trait object lifetime

Can anyone tell what the problem is with the following code? 任何人都可以通过以下代码告诉问题是什么? The compiler is complaining about lifetimes, but the error message makes absolutely no sense. 编译器抱怨生命周期,但错误消息绝对没有意义。 I've tried everything I could think of, but nothing seems to help. 我已经尝试了我能想到的一切,但似乎没有任何帮助。

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

The compiler error is 编译器错误是

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

Which makes absolutely no sense. 这绝对没有意义。 How is the borrower outliving anything? 借款人的生活如何? I'm not even using the borrowed value! 我甚至没有使用借来的价值!

Ok, this does make sense, but it's hard to see due to lifetime elision. 好吧,这确实有意义,但由于终生的缺席而很难看到。 So, here's your code with all the lifetimes written out explicitly, and with irrelevant details culled: 所以,这是你的代码,明确地写出所有生命周期,并且无关紧要的细节被剔除:

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

Now, from the perspective of the last line of parent , we can work out the following equivalences by just reading the definition of user and substituting the lifetimes we have in parent : 现在,从parent的最后一行的角度来看,我们可以通过读取user的定义并替换parent的生命周期来计算出以下等价:

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

Furthermore, this lets us conclude that: 此外,这让我们得出结论:

  • 'x = 'i 'x = 'i

This is the problem. 这就是问题。 Because of the way you've defined user , you've put yourself in a situation where the lifetime of the pool_ref borrow (which is equal to the lifetime of the pool storage location you're borrowing from) must be the same as the lifetime 'x being used in the thing being stored in pool . 由于您定义user的方式,您已经处于这样的情况: pool_ref借用的生命周期(等于您pool_ref借用的pool存储位置的生命周期)必须与生命周期相同'x用于存储pool的东西。

It's a bit like the Box being able to have a pointer to itself before it exists, which doesn't make any sense. 它有点像Box能够在它存在之前有一个指向它自己的指针,这没有任何意义。

Either way, the fix is simple. 无论哪种方式,修复都很简单。 Change user to actually have the correct type: user更改为实际具有正确的类型:

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

This matches the type produced by new . 这与new生成的类型相匹配。 Alternately, just don't use borrow_mut : 或者, 只是不要使用borrow_mut

user(&mut *pool)

This works because it is "re-borrowing". 这是有效的,因为它是“重新借用”。 Calling borrow_mut translates the lifetimes more or less directly, but re-borrowing allows the compiler to narrow the borrows to shorter lifetimes. 调用borrow_mut或多或少直接转换生命周期,但重新借用允许编译器将借用缩小到更短的生命周期。 To put it another way, explicitly calling borrow_mut doesn't allow the compiler enough freedom to "fudge" the lifetimes to make them all line up, re-borrowing does . 换一种方式,显式调用borrow_mut不允许编译器足够的自由来“忽悠”的寿命,使他们都排队,再借贷一样

As a quick aside: 暂时不谈:

I'm not even using the borrowed value! 我甚至没有使用借来的价值!

Irrelevant. 无关紧要。 Rust does type- and lifetime-checking entirely locally. Rust 完全在本地进行类型和生命周期检查。 It never looks at the body of another function to see what it's doing; 永远不会看另一个函数的主体来看它正在做什么; it goes on the interface alone. 它仅在界面上进行。 The compiler neither checks, nor cares, what you're doing inside a different function. 编译器不检查,也不关心,你是不同的函数里面做什么。

Note that there's more to the error message: 请注意,错误消息还有更多内容:

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();
   |>                          ^

Let's look at user : 我们来看看user

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

This says that it will accept a mutable reference (with an unnamed lifetime) to a trait object parameterized with the lifetime 'a . 这表示它将接受一个带有生命周期'a参数化的特征对象的可变引用(具有未命名的生命周期)。

Turning to new , I'd say the method is highly suspicious: 谈到new ,我会说这种方法非常可疑:

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

This says that it will return a boxed trait object with whatever lifetime the caller specifies . 这表示它将返回一个盒装特征对象,其中包含调用者指定的任何生命周期 That basically never makes sense . 这基本上没有意义

All that said, I'm not clear why the code chooses to use borrow_mut . 总而言之,我不清楚为什么代码选择使用borrow_mut I would have written that more directly: 我会写得更直接:

user(&mut *pool);

This dereferences the Box<Trait> to get a Trait , then takes a mutable reference, yielding &mut Trait , which compiles. 这取消了Box<Trait>以获得Trait ,然后获取一个可变引用,产生&mut Trait ,它编译。

I cannot currently explain why BorrowMut differs in behavior. 我目前无法解释为什么BorrowMut的行为不同。

I'm not sure why this error happens, but I can give solutions! 我不确定为什么会发生这种错误,但我可以给出解决方案!

First, it seems that using borrow_mut unnecessarily restricts the lifetime of the returned reference. 首先,似乎使用borrow_mut不必要地限制返回引用的生命周期。 Using operators to create the reference solves the error. 使用运算符创建引用可以解决错误。

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

However, if we don't do that, we can solve the error by adding a lifetime bound to the Trait object in user 's obj argument. 但是,如果我们这样做,我们可以通过在userobj参数中添加生命绑定到Trait对象来解决错误。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM