1

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?

Gerharddc
  • 3,921
  • 8
  • 45
  • 83
  • Why `&'a self` and not `&self` like most methods? If you remove `'a` from everywhere entirely, you'll likely be much closer to something that works. – loganfsmyth Mar 06 '21 at 08:50
  • @loganfsmyth doing that just results in the error "these two types are declared with different lifetimes... ...but data from `self` flows into `render_pass` here" – Gerharddc Mar 06 '21 at 08:57
  • Please try to create a [mre]. There are a few issues with your code that I can see but no smoking gun. Since the playground doesn't support wgpu, try to recreate the problem in an otherwise empty Cargo project and post the `[dependencies]` section of `Cargo.toml` along with the minimized source code. – trent Mar 06 '21 at 13:29
  • First issue: `Node` doesn't appear to need two lifetime parameters; one would suffice, unless you're doing something tricky elsewhere in the code. The reason for this is because `'a` and `'b` are the lifetimes of borrows, not of the underlying objects. If `'a: 'b`, and the caller of `render` is allowed to choose both `'a` and `'b`, it can (given the signature of `render`) always just choose `'a` = `'b` without loss of generality. The only reason for needing two parameters is if some of your `impl`s use `'a` in some other way, which -- if it's happening -- is probably not what you meant. – trent Mar 06 '21 at 13:36
  • Second issue, probably related: `&'a dyn Node<'a, 'b>`. Traits are [invariant](/q/53960041/3650362) over their type/lifetime parameters, so this has the same problem as [`&'a mut &'a ()`](/q/32165917/3650362): it means that the implementor of `Node` has to be borrowed for its *entire* lifetime -- the caller of `RenderArea::render` is *not* free to choose `'a`, and when it in turn calls `Node::render`, it has to borrow `node` for the same lifetime `'a`, instead of some shorter lifetime. – trent Mar 06 '21 at 13:49
  • Third issue, maybe: you're putting lifetime parameters only on `impl`s where they could be on `fn`s. This isn't necessarily a problem but it seems to me that `Node` would work fine as `trait Node { fn render<'b>(&'b self, render_pass: &mut wgpu::RenderPass<'b>); }` (i.e., no lifetimes on `Node` itself at all). That would probably allow you to remove the lifetime parameters on `RenderArea::render` as well. In general, if you can avoid putting lifetime parameters on *types* and *traits*, and only put them on *functions* when absolutely required, you maximize your chances of working code. – trent Mar 06 '21 at 14:00
  • Side note: use `#![warn(rust_2018_idioms)]` at the top of the crate root (`main.rs` and/or `lib.rs`) to get a warning when you forget a `<'_>` on something like `RenderPass`. This should be the default, frankly. – trent Mar 06 '21 at 14:07
  • @trentcl removing lifetime annotations from the trait and putting one on the function itself fixed it, thanks! For some reason I did not realise functions can have lifetime annotations – Gerharddc Mar 06 '21 at 15:33

1 Answers1

2

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, &[]);
        ...
    }
}
Gerharddc
  • 3,921
  • 8
  • 45
  • 83