Why does Rust seem to ignore my lifetime annotation?

221 Views Asked by At

I have the following class

pub struct RectangleNode {
    ...
    uniform_bind_group: wgpu::BindGroup,
    ...
}

Which should implement the following trait

pub trait Node<'a, 'b> where 'a : 'b {
    fn render(&'a self, render_pass: &'b mut wgpu::RenderPass);
}

And I am trying to do it as follows

impl<'a, 'b> node::Node<'a, 'b> for RectangleNode where 'a : 'b {
    fn render(&'a self, render_pass: &'b mut wgpu::RenderPass) {
        ...
        render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
        ...
    }
}

And this (obviously?) gives me the following lifetime error

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
   --> src/rectangle_node.rs:153:39
    |
153 |         render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 148:6...
   --> src/rectangle_node.rs:148:6
    |
148 | impl<'a, 'b> node::Node<'a, 'b> for RectangleNode where 'a : 'b {
    |      ^^
note: ...so that reference does not outlive borrowed content
   --> src/rectangle_node.rs:153:39
    |
153 |         render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the method body at 149:5...
   --> src/rectangle_node.rs:149:5
    |
149 |     fn render(&'a self, render_pass: &'b mut wgpu::RenderPass) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the types are compatible
   --> src/rectangle_node.rs:153:21
    |
153 |         render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
    |                     ^^^^^^^^^^^^^^
    = note: expected `&mut RenderPass<'_>`
               found `&mut RenderPass<'_>`

But I am struggling to understand what the issue is. I have tried to declare that the RectangleNode should have a longer lifetime than the RenderPass as I think the problem is that Rust needs to be sure that this is the case, yet it seems to be ignoring my lifetime annotation on RenderPass and assuming an anonymous lifetime for it?

EDIT:

I could fix the original issue by changing to

impl<'a, 'b> node::Node<'a, 'b> for RectangleNode where 'a : 'b {
    fn render(&'a self, render_pass: &mut wgpu::RenderPass<'b>) {
        ...
        render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
        ...
    }
}

But that has now lead to another problem down the line where I try to use the the following code

impl<'a, 'b> RenderArea where 'a : 'b {
    pub fn render(&mut self, node: &'a dyn Node<'a, 'b>) -> Result<(), wgpu::SwapChainError> {
        let frame = self.swap_chain.get_current_frame()?.output;

        let mut encoder = self
            .device
            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
                label: Some("Render Encoder"),
            });

        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
                attachment: &frame.view,
                resolve_target: None,
                ops: wgpu::Operations {
                    load: wgpu::LoadOp::Clear(self.bg_color),
                    store: true,
                },
            }],
            depth_stencil_attachment: None,
        });

        node.render(&mut render_pass);
        drop(render_pass);

        self.queue.submit(std::iter::once(encoder.finish()));
        Ok(())
    }
}

Which then gives the following errors

error[E0597]: `encoder` does not live long enough
   --> src/render_area.rs:107:31
    |
96  |   impl<'a, 'b> RenderArea where 'a : 'b {
    |            -- lifetime `'b` defined here
...
107 |           let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
    |                                 -^^^^^^
    |                                 |
    |  _______________________________borrowed value does not live long enough
    | |
108 | |             color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
109 | |                 attachment: &frame.view,
110 | |                 resolve_target: None,
...   |
116 | |             depth_stencil_attachment: None,
117 | |         });
    | |__________- assignment requires that `encoder` is borrowed for `'b`
...
124 |       }
    |       - `encoder` dropped here while still borrowed

error[E0713]: borrow may still be in use when destructor runs
   --> src/render_area.rs:109:29
    |
96  |   impl<'a, 'b> RenderArea where 'a : 'b {
    |            -- lifetime `'b` defined here
...
107 |           let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
    |  _______________________________-
108 | |             color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
109 | |                 attachment: &frame.view,
    | |                             ^^^^^^^^^^^
110 | |                 resolve_target: None,
...   |
116 | |             depth_stencil_attachment: None,
117 | |         });
    | |__________- assignment requires that `frame.view` is borrowed for `'b`
...
124 |       }
    |       - here, drop of `frame` needs exclusive access to `frame.view`, because the type `SwapChainTexture` implements the `Drop` trait

error[E0505]: cannot move out of `encoder` because it is borrowed
   --> src/render_area.rs:122:43
    |
96  |   impl<'a, 'b> RenderArea where 'a : 'b {
    |            -- lifetime `'b` defined here
...
107 |           let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
    |                                 -------
    |                                 |
    |  _______________________________borrow of `encoder` occurs here
    | |
108 | |             color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
109 | |                 attachment: &frame.view,
110 | |                 resolve_target: None,
...   |
116 | |             depth_stencil_attachment: None,
117 | |         });
    | |__________- assignment requires that `encoder` is borrowed for `'b`
...
122 |           self.queue.submit(std::iter::once(encoder.finish()));
    |                                             ^^^^^^^ move out of `encoder` occurs here

To me this error seems to imply that my lifetime annotations indicate that I want to borrow the render_pass passed into my render function for the lifetime of the node, but this is not want to do at all. I am at a loss for how to annotate this properly, any ideas?

1

There are 1 best solutions below

0
On BEST ANSWER

Thanks to @trentcl for pointing out the solution. I did not realize that functions can have lifetime annotations independently from traits, so I came up with the very convoluted annotations which did not convey my intent properly. Simplifying to the following fixed my issue

pub trait Node {
    fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>);
}
...
impl node::Node for RectangleNode {
    fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
        ...
        render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
        ...
    }
}