I'm working on a toy ray tracer project in Rust and am hung up on a lifetime-related error. I've stripped down my code to the following self-contained failing case:
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] };
}
which complains
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)
I want to use traits to define objects in the scene, but I want the Scene
object to own them. My current understanding is that this means I need Box
or something equivalent because trait objects are of unknown size.
I also want objects to share references to Material
s, since there won't be that many of them and though they're relatively simple and Copy
able, I don't want literally tens or hundreds of thousands of identical copies of the same thing (hence using &'a Material
).
I'm confused why it's problematic to pass &material
here though: since values are dropped latest-first, wouldn't Scene
be dropped first, allowing boxed_sphere
to be dropped (since it now owns a Vec
that owns that Box
), which it then is, allowing material
to be dropped, no problem? It seems like it should live at least as long as the other two values in the function, since I'm holding onto the value with the name material
for the scope of the whole function.
Also somewhat confusingly, commenting out the instantiation of Scene
fixes the issue for reasons I don't understand.