首页 文章

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

提问于
浏览
6

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

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

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

3 回答

  • 4

    好吧,这确实有意义,但它是明确写出所有生命周期的代码,并且无关紧要的细节被剔除:

    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)
    

    这是有效的,因为它是"re-borrowing" . 调用 borrow_mut 或多或少地直接转换生命周期,但重新借用允许编译器将借用缩小到更短的生命周期 . 换句话说,显式调用 borrow_mut 不允许编译器有足够的自由度来使它们全部排成一行,重新借用 .

    暂时不谈:

    我甚至没有使用借来的 Value !

    无关紧要 . Rust完全在本地进行类型和生命周期检查 . 它永远不会查看另一个函数的主体,看看它在另一个函数中做了什么 .

  • 2

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

    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 ,我会说这个方法是 highly 可疑的:

    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 的行为不同 .

  • 10

    我不确定为什么会发生这种错误,但我可以给出解决方案!

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

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

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

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

相关问题