编译器建议我添加一个“静态生命周期”,因为参数类型可能无法长时间存活,但我不认为这是我想要的。

71

我正在尝试实现类似于这个最小化示例的东西:

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(而不仅仅是trait Bar<T>)。 移动到Box中后,盒子内部项的生命周期应与盒子的实际生命周期绑定(例如从向量中pop()时,对象将被销毁)。

但是,编译器显然不同意(我相信它比我知道更多...),要求我考虑添加'static生命周期限定符(E0310)。 我确信99%那不是我想要的,但我不确定我应该做什么。

为澄清我的想法并帮助识别误解,来自C ++背景的我的心理模型是:

  • Box<T>本质上是std :: unique_ptr<T>
  • 如果Copy,则变量按值传递,否则按rvalue-reference传递
  • 具有引用注释的话,&大致相当于const&,而&mut大致相当于&
  • 默认生命周期是词法范围
2个回答

91

查看完整错误信息:

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

那将是坏消息。

你需要静态生命周期还是参数化生命周期取决于你的需求。静态生命周期更容易使用,但有更多限制。因此,当你在结构体或类型别名中声明trait object时,默认为静态生命周期:

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
    // same as
    // data: Vec<Box<dyn Bar<T> + 'static>>,
} 

然而,当作为参数使用时,特质对象使用生命周期省略并获得唯一的生命周期:

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

8
我无法表达这个解释有多么有价值。非常感谢! - AlexLiesenfeld

32

他要求我考虑添加一个“静态生存期限定符(E0310)”。 我99%确定这不是我想要的,但我不确定应该做什么。

是的。编译器不需要&'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));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接