Borrowed in previous iteration of loop error using generics, but not using impl

I have a reproduction of this issue here.

The (simplified) scenario is I have a Renderer trait which takes a mutable reference to a PixelWriter implementation in its render function. In the runner code, I call this render function in a loop, and for each iteration I wrap the main PixelWriter implementation in an OffsetPixelWriter before passing it to the render function.

OffsetPixelWriter simply contains a mutable reference to a PixelWriter and an offset:

struct OffsetPixelWriter<'w, W>
where
    W: PixelWriter,
{
    pub pixel_writer: &'w mut W,
    pub offset_x: usize,
    pub offset_y: usize,
}

If I define the Renderer trait using impl then the runner code all works fine:

trait RendererImpl {
    fn render(&self, x: usize, y: usize, pixel_writer: &mut impl PixelWriter);
}

struct RunnerImpl<R: RendererImpl> {
    renderer: R
}

// This compiles fine.
impl<R: RendererImpl> RunnerImpl<R> {
    pub fn run(&self) {
        let offsets = vec![10, 20, 30];
        let mut pixel_writer = MockPixelWriter{};
        
        for offset in offsets {
            let mut offset_writer = OffsetPixelWriter {
                pixel_writer: &mut pixel_writer,
                offset_x: offset,
                offset_y: 0,
            };
            
            self.renderer.render(0, 0, &mut offset_writer);
        }
    }
}

However if I define Renderer using generics, it fails to compile:

trait RendererGeneric<PW: PixelWriter> {
    fn render(&self, x: usize, y: usize, pixel_writer: &mut PW);
}

struct RunnerGeneric<'pw, R: RendererGeneric<OffsetPixelWriter<'pw, MockPixelWriter>>> {
    renderer: R,
    _phantom: std::marker::PhantomData<&'pw ()>,
}

// This does not compile.
impl<'pw, R: RendererGeneric<OffsetPixelWriter<'pw, MockPixelWriter>>> RunnerGeneric<'pw, R> {
    pub fn run(&self) {
    
        let offsets = vec![10, 20, 30];
        let mut pixel_writer = MockPixelWriter{};
        
        for offset in offsets {
            // error[E0499]: cannot borrow `pixel_writer` as mutable more than once at a time
            let mut offset_writer = OffsetPixelWriter {
                pixel_writer: &mut pixel_writer,
                offset_x: offset,
                offset_y: 0,
            };
            
            self.renderer.render(0, 0, &mut offset_writer);
        }
    }
}

The error is:

error[E0499]: cannot borrow `pixel_writer` as mutable more than once at a time
  --> src/main.rs:50:31
   |
41 |   impl<'pw, R: RendererGeneric<OffsetPixelWriter<'pw, MockPixelWriter>>> RunnerGeneric<'pw, R> {
   |        --- lifetime `'pw` defined here
...
49 |               let mut offset_writer = OffsetPixelWriter {
   |  _____________________________________-
50 | |                 pixel_writer: &mut pixel_writer,
   | |                               ^^^^^^^^^^^^^^^^^ `pixel_writer` was mutably borrowed here in the previous iteration of the loop
51 | |                 offset_x: offset,
52 | |                 offset_y: 0,
53 | |             };
   | |_____________- assignment requires that `pixel_writer` is borrowed for `'pw`

I thought the compiler would be able to figure out that the 'pw lifetime could be constrained to a single loop iteration, but I'm obviously missing something.

Why does the generic version not work, and how would I make it work without going back to impl?

Rust Playground Reproduction.

Any lifetime declared in fn or impl generics is freely chosen by the caller and can't ever be accurately referring to the life of values created within the function body.

What you want to do instead is remove the parameter and PhantomData, then declare that the trait bound holds for any lifetime (including the lifetime of things created within the loop), with a HRTB:

impl<R> RunnerGeneric<R> where
    R: for<'pw> RendererGeneric<OffsetPixelWriter<'pw, MockPixelWriter>>
{

Playground

3 Likes

@kpreid Thank you, that makes total sense.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.