我正在研究Rust中的玩具光线追踪项目,并且因为与生命有关的错误而陷入困境。我已将我的代码删除到以下自包含的失败案例中:
struct Material {}
pub struct Sphere<'a> {
material: &'a Material,
}
pub trait AnySceneObject {}
impl<'a> AnySceneObject for Sphere<'a> {}
pub struct Scene {
objects: Vec<Box<AnySceneObject>>,
}
fn main() {
let material = Material {};
let boxed_sphere: Box<AnySceneObject> = Box::new(Sphere { material: &material });
Scene { objects: vec![boxed_sphere] };
}
抱怨
error[E0597]: `material` does not live long enough
--> main.rs:17:74
|
17 | let boxed_sphere: Box<AnySceneObject> = Box::new(Sphere { material: &material });
| ^^^^^^^^ does not live long enough
18 | Scene { objects: vec![boxed_sphere] };
19 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to previous error(s)
我想使用traits来定义场景中的对象,但我希望Scene
对象拥有它们。我目前的理解是,这意味着我需要Box
或类似的东西,因为特质对象的大小未知。
我也想让对象分享对Material
的引用,因为它们不会有那么多,虽然它们相对简单且Copy
能够,但我不想要几十个或相同数量的相同副本(因此使用&'a Material
)。
我很困惑为什么在这里传递&material
是有问题的:因为最新优先删除了值,所以不会先删除Scene
,允许boxed_sphere
删除({1}}因为它现在拥有一个拥有Vec
)的Box
,它就是这样,允许material
被删除,没问题?看起来它应该至少与函数中的其他两个值一样长,因为我保留了整个函数范围的名称为material
的值。
同样有些令人困惑的是,评论Scene
的实例化解决了这个问题,原因我不明白。
答案 0 :(得分:4)
首先,如果你有数十万个场景对象,把它们放在一个盒子里(基本上就是一个堆对象)绝对不是一个好主意。
调用错误是因为Box
的内容不得包含任何可能过期的引用。您可以移动Box
,它可能永远不会被删除,直到流程结束,因此它所拥有的任何引用都必须具有'static
生命周期。
您可以使用Box<T + 'a>
来表明它的生命周期有限:
pub struct Scene<'a> {
objects: Vec<Box<AnySceneObject + 'a>>,
}
您还可以使用Vec<&Trait>
存储对实现特征的不同对象的引用集合。以下代码编译:
pub struct Scene<'a> {
objects: Vec<&'a AnySceneObject>,
}
fn main() {
let material = Material {};
let sphere = Sphere { material: &material };
Scene {
objects: vec![&sphere]
};
}
如果您了解特征的所有可能实现,则可以用枚举替换它。这将使代码更高效,因为您将拥有一个拥有枚举而不是引用的向量。