我想实现一些东西,看起来像这个最小的例子:
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));
}
}