I want to init a variable lazily inside a loop (in every iteration I check whether it's been initialized already, so I know the initialization code runs at most once, but the borrow checker doesn't believe me).
Is there a type/macro/programming pattern for this in Rust?
Specifically, I'm encoding a gif from a list of frame files. The encoder needs to outlive the loop, but also needs to know the image size up front, but I know image size only inside my loop:
fn write_to<W>(output_writer: W) {
let mut encoder = None;
for f in files {
let image = load_image(f);
if encoder.is_none() {
encoder = Some(Encoder::new(output_writer /*moved!*/, image.width, image.height)));
}
encoder.add_frame(image);
}
}
If I use Option<Encoder> for the encoder, it doesn't borrow check, because output_writer used to construct the encoder could be moved multiple times in the loop.
I don't see any other solution than duplicating/unrolling the first iteration of the loop. Is there another solution?
enum State<W> {
Uninitialized(W),
Initialized(Encoder),
}
impl<W> State<W> {
fn add_frame(self, image: Image) -> Self {
let mut encoder = match self {
State::Initialized(encoder) => encoder,
State::Uninitialized(writer) => {
Encoder::initialize(writer, image.width, image.height)
}
};
encoder.add_frame(image);
State::Initialized(encoder)
}
}
//------------------------------------------------
fn write_to<W>(output_writer: W)
{
let mut state = State::Uninitialized(output_writer);
for () in vec![/* ... */] {
let image = unimplemented!();
state = state.add_frame(image);
}
}
I don’t see any other solution than duplicating/unrolling the first iteration of the loop. Is there another solution?
For a short loop like your code sample (the only duplicated code would be a call to load_image), honestly, I would just unroll the first iteration, because it's much more direct. But somehow I imagine that your actual code is not so short.