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
?