In this mesh class, what's wrong with my use of lifetimes?

I'm trying to create a basic wrapper around a mesh to work with wgpu (more will be added later β€” abstracting this is justified), but I'm having some trouble with the lifetimes. This is what I have right now:

pub struct RenderableMesh{
    pub buffer: Buffer,
    pub count: u32
}

impl RenderableMesh{

    pub fn render<'a, 'b>(&'a self, render_pass: &'b mut RenderPass<'b>) where 'b: 'a{
        render_pass.set_vertex_buffer(0, &self.buffer, 0, 0);
        render_pass.draw(0..self.count, 0..1);
    }

}

Yet, this gives me an error:

   |
76 |     pub fn render<'a, 'b>(&'a self, render_pass: &'b mut RenderPass<'b>) where 'b: 'a{
   |                           --------               ----------------------
   |                           |
   |                           these two types are declared with different lifetimes...
77 |         render_pass.set_vertex_buffer(0, &self.buffer, 0, 0);
   |                     ^^^^^^^^^^^^^^^^^ ...but data from `self` flows into `render_pass` here

The error seems to be saying that the code is malformed since render_pass can store data from self, and that self may be dropped first, but the where 'b: 'a part would seem to contradict that, since it (I think) says that render_pass's lifetime is smaller than that of self's. What's goign on here (is rustc's borrow checker unable to verify an otherwise valid program as I suspect)? How can I fix it?

I think you’ve got the relationship here backwards. This means that β€œβ€™b outlives ’a”, so you’re releasing the shared access to self before releasing your exclusive access to render_pass.

When I switch the lifetime inheritance in the function's where clause, the function itself compiles fine, but use of it isn't possible, even when render_pass's lifetime is much shorter than the lifetime of the self reference. The call takes place in a block inside a function that takes &mut self, which has my RenderableMesh instance as a membe. The lifetime of the render_pass is the inner block, so its lifetime is clearly contained by the lifetime of the &mut self reference to my outer function, thus the code should compile, but it gives me this error:

    |
171 |             self.mesh.render(&mut render_pass);
    |                              ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
172 |         }
    |         -
    |         |
    |         `render_pass` dropped here while still borrowed
    |         borrow might be used here, when `render_pass` is dropped and runs the `Drop` code for type `wgpu::RenderPass`

I can almost guarantee the problem is &'b mut RenderPass<'b>.

&'b Foo<'b> (a reference to a thing with the same lifetime inside it) is suspicious enough to pay close attention to. But &'b mut Foo<'b> (same thing but with a mutable reference) is virtually always a mistake. The reason they're different has to do with variance and, fair warning, when the answer to your question contains the word "variance" you're probably going to have a bad time. I'll link to this SO question if you have an hour to kill. The upshot is that taking a &'b mut RenderPass<'b> means that the RenderPass is borrowed for its entire lifetime, and it can't be used anymore. You need to let the compiler choose different lifetimes.

Let's back up. Starting with no lifetimes at all:

pub fn render(&self, render_pass: &mut RenderPass<'_>)

What compiler error does this give you?

5 Likes

Thanks for your help! It allowed me to figure this out, which works perfectly:

pub fn render<'a, 'b>(&'a self, render_pass: &'b mut RenderPass<'a>) where 'a: 'b ...
2 Likes