1

I'm working on a hobby game engine, and I have a struct Mesh and a view over it with Instance:

struct Mesh {
    // Vertex info, etc
}

struct Instance<'a> {
    mesh: &'a Mesh,
    transform: Mat4,
}

This seems to me to be exactly what the lifetime rules are for; I'm declaring that the Instance has to live a shorter life than the Mesh it's holding a reference to.

When I try to use this in my main function:

fn main() {
    let mesh = Mesh::new();

    // Add vertices, etc

    // Scene has a Vec<Box<dyn Drawable>>, which Instance implements.
    let mut scene = Scene::new(glam::Mat4::zero());
    scene.push(Box::new(Instance::new(&mesh, glam::Mat4::zero())));

    render_scene(scene)
}

I get the following error from the borrow checker:

error[E0597]: `mesh` does not live long enough
  --> bin/rendertest.rs:9:39
   |
9  |     scene.push(Box::new(Instance::new(&mesh, glam::Mat4::zero())));
   |                -----------------------^^^^^----------------------
   |                |                      |
   |                |                      borrowed value does not live long enough
   |                cast requires that `mesh` is borrowed for `'static`
...
12 | }
   | - `mesh` dropped here while still borrowed

What is the cast it's talking about? And why does it need the mesh to be 'static? It seems like the mesh should live past the return of render_scene, when main exits.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
colinmarc
  • 2,421
  • 1
  • 22
  • 34
  • Does this answer your question? [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](https://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no) – E_net4 Jun 30 '20 at 13:20

2 Answers2

2

The key problem here is that Box<dyn T> is equivalent to Box<dyn T + 'static>. But that's just the default lifetime - you can override it by writing Box<dyn T + 'a>.

The only thing you need to change is to give a lifetime to the Scene and pass that down to the contained Boxes.

struct Scene<'a> {
    entries: Vec<Box<dyn Drawable + 'a>>,
}

impl<'a> Scene<'a> {
    pub fn new() -> Scene<'a> {
        Scene { entries: vec![] }
    }
}

A full example is available on the playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • This works, thanks! But it means that anything that contains a `Scene` has to have a lifetime parameter, too. And it's not true that the scene can't outlive the mesh; in practice it almost certainly will, but you could imagine dropping the mesh from the vec. Is there a simpler way? Maybe making the meshes static? – colinmarc Jun 30 '20 at 06:09
  • 1
    You've said that the Instance can't last longer than the Mesh. And the Scene owns the Instances - so the Scene can't last longer than the Instance, and thus can't last longer than the Mesh. If you want something else - then you'll need to think about your lifetimes carefully. You might end up wanting to use Rc instead of Box for some things. – Michael Anderson Jun 30 '20 at 06:31
  • If the Scene drops the Instance from entries (and drops it, period), it could outlive the Instance, right? So it'd be nice if there were some way to do that. But maybe that deserves its own question. – colinmarc Jun 30 '20 at 07:13
0

In the end, I found it easiest to use std::rc::Rc to make an Instance:

struct Instance {
    mesh: Rc<Mesh>,
    transform: Mat4
}

Semantically, this doesn't really match what was intended: meshes don't need to be dropped until the game is finished running, so I don't need to count references to them. But it saved me from having to add an explosion of explicit lifetimes to the Scene object and anything else that has a Scene.

colinmarc
  • 2,421
  • 1
  • 22
  • 34