编译器建议我添加一个"静态生存期",因为参数类型的生存期可能不够长,但我认为这不是我想要的



我想实现一些东西,看起来像这个最小的例子:

trait Bar<T> {}
struct Foo<T> {
    data: Vec<Box<Bar<T>>>,
}
impl<T> Foo<T> {
    fn add<U: Bar<T>>(&mut self, x: U) {
        self.data.push(Box::new(x));
    }
}

因为Rust默认是(据我所知)所有权传递,我的心智模型认为这应该是可行的。add方法获得对象x的所有权,并且能够将该对象移动到Box中,因为它知道完整的类型U(而不仅仅是特性Bar<T>)。一旦移动到Box中,盒子内物品的生命周期应该与盒子的实际生命周期绑定(例如,当pop()偏离向量时,对象将被销毁)。

显然,编译器不同意(我肯定知道的比我多一点……),要求我考虑添加'static生命周期限定符(E0310)。我百分之九十九肯定那不是我想要的,但我不完全确定我应该做什么。

为了澄清我的想法并帮助识别误解,我的心智模型是:

来自c++背景:
  • Box<T>实际上是std::unique_ptr<T>
  • 没有任何注释,如果Copy,变量按值传递,否则按右值引用传递
  • 使用参考注释,&大致为const&, &mut大致为&
  • 默认生存期为词法作用域

查看整个错误:

error[E0310]: the parameter type `U` may not live long enough
 --> src/main.rs:9:24
  |
8 |     fn add<U: Bar<T>>(&mut self, x: U) {
  |            -- help: consider adding an explicit lifetime bound `U: 'static`...
9 |         self.data.push(Box::new(x));
  |                        ^^^^^^^^^^^
  |
note: ...so that the type `U` will meet its required lifetime bounds
 --> src/main.rs:9:24
  |
9 |         self.data.push(Box::new(x));
  |                        ^^^^^^^^^^^

具体来说,编译器让您知道某些任意类型U 可能包含引用,并且该引用随后可能无效:

impl<'a, T> Bar<T> for &'a str {}
fn main() {
    let mut foo = Foo { data: vec![] };
    {
        let s = "oh no".to_string();
        foo.add(s.as_ref());
    }
}

那将是坏消息。

您是想要'static生命周期还是参数化生命周期取决于您的需要。'static生命周期更容易使用,但有更多的限制。因此,当您在结构体或类型别名中声明trait对象时,它是默认的:

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
    // same as
    // data: Vec<Box<dyn Bar<T> + 'static>>,
} 
然而,当用作参数时,trait对象使用生命周期省略并获得唯一的生命周期:
fn foo(&self, x: Box<dyn Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<dyn Bar<T> + 'b>)

这两个东西需要匹配。

struct Foo<'a, T> {
    data: Vec<Box<dyn Bar<T> + 'a>>,
}
impl<'a, T> Foo<'a, T> {
    fn add<U>(&mut self, x: U)
    where
        U: Bar<T> + 'a,
    {
        self.data.push(Box::new(x));
    }
}

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
    fn add<U>(&mut self, x: U)
    where
        U: Bar<T> + 'static,
    {
        self.data.push(Box::new(x));
    }
}

要求我考虑添加"静态生命周期限定符"(E0310)。我百分之九十九肯定那不是我想要的,但我不完全确定我应该做什么。

是的。编译器不需要&'static引用,它需要U: 'static

拥有U: 'static意味着U不包含生存期小于'static的引用。这是必需的,因为您希望将U实例放在没有生命周期的结构中。

trait Bar<T> {}
struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
    fn add<U: Bar<T> + 'static>(&mut self, x: U) {
        self.data.push(Box::new(x));
    }
}

最新更新