First of all, my question is different from this StackOverflow question and this Rust forum thread because it does not return or push anything out of the block that uses the reference. To me, the error shown later here in this question makes no sense. I probably understood some key concept wrong, but anyway...
I am using the gif crate and I needed to store a Decoder
and a Frame
(that is probably not relevant, but there may be a better way to solve this problem), but had some problems with references.
Specifically, the compiler is accusing me of borrowing as mutable mutiple times in the loop, but it seems for me that all references were dropped. Here's a minimal example with part of the gif code:
use simplified_gif_crate::{Decoder, Frame};
pub struct Gif<'a> {
current_frame: Option<&'a Frame>,
decoder: Box<Decoder>,
}
impl<'a> Gif<'a> {
pub fn new(decoder: Box<Decoder>) -> Self {
Self {
current_frame: None,
decoder,
// next_frame_at: std::time::Instant,
}
}
/// Returns None if there are no more frames
pub fn current_frame(&'a mut self /* now: std::time::Instant */) -> Option<&Frame> {
/* That's why I need to store the `Frame`. If it's not time for the next frame, `cur_frame` will be returned. */
// if now < self.next_frame_at {
// return self.cur_frame;
// }
if let Some(frame) = self.decoder.read_next_frame() {
self.current_frame = Some(frame);
// self.next_frame_at += Self::FRAME_DELAY;
Some(frame)
} else {
None
}
}
}
pub struct App<'a> {
pub active_gifs: Vec<Gif<'a>>, // Gifs that will need to be drawn
// pub resources: Resources,
// ... [More app-related stuff]
}
impl App<'_> {
fn init() -> Self {
Self {
active_gifs: Vec::new(),
}
}
fn add_gif(&mut self) {
self.active_gifs.push(Gif::new(self.get_decoder()));
}
fn get_decoder(&self) -> Box<Decoder> {
// In fact, it gets the real `Decoder<File>` from the `App::resources` field.
// But I'll simplify it here
Box::new(Decoder::new(vec![11, 22, 33]))
}
}
fn main() {
let mut app = App::init();
app.add_gif();
for i in 0..app.active_gifs.len() {
if let Some(frame) = app.active_gifs[i].current_frame() {
println!("Frame buffer: {:?}", frame.buffer);
} else {
app.active_gifs.remove(i);
}
}
}
// This a simplification of the structs in the crate
mod simplified_gif_crate {
// The real one reads a file
pub struct Decoder {
data: std::collections::VecDeque<u8>,
current_frame: Frame,
}
impl Decoder {
pub fn new(data: Vec<u8>) -> Self {
Self {
data: data.into(),
current_frame: Frame::default(),
}
}
pub fn read_next_frame(&mut self) -> Option<&Frame> {
if let Some(frame) = self.data.pop_front() {
// Of course, in the real crate that contains the pixels of the frame,
// and not just a `Vec` with a single byte
self.current_frame = Frame { buffer: vec![frame] };
Some(&self.current_frame)
} else {
None
}
}
}
pub struct Frame {
pub buffer: Vec<u8>,
}
impl Default for Frame {
fn default() -> Self {
Self {
buffer: vec![],
}
}
}
}
When trying to compile that code, the compiler returns these errors:
error[E0499]: cannot borrow `app.active_gifs` as mutable more than once at a time
--> src/main.rs:64:30
|
64 | if let Some(frame) = app.active_gifs[i].current_frame() {
| ^^^^^^^^^^^^^^^
| |
| `app.active_gifs` was mutably borrowed here in the previous iteration of the loop
| first borrow used here, in later iteration of loop
error[E0499]: cannot borrow `app.active_gifs` as mutable more than once at a time
--> src/main.rs:69:13
|
64 | if let Some(frame) = app.active_gifs[i].current_frame() {
| --------------- first mutable borrow occurs here
...
69 | app.active_gifs.remove(i);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
Why does that happen? To show why I think it should work, I will break down the important parts of the code above to an invalid code whose only purpose is to represent what I understand that happens step-by-step:
let mut app: App = App {
active_gifs: Vec {
gif_ptr: heap_alloc(),
len: 0,
}
}
app.active_gifs.gif_ptr[0] = Gif {
current_frame: None,
decoder: Box::new(Decoder::new()),
};
app.active_gifs.len += 1;
app.active_gifs.gif_ptr[1] = Gif {
current_frame: None,
decoder: Box::new(Decoder::new()),
};
app.active_gifs.len += 1;
app.active_gifs.len
// All references created and dropped will be commented below
// I will also skip `Option`s and `if let`s to reduce clutter, but they are still there
{ // `i`, for example, cannot be accessed from outside of here, so there is an implicit block
let i: usize = 0
let end: usize = app.active_gifs.len;
loop {
if i >= end {
break;
}
let cur_frame: &Frame; // Had to change the name to prevent shadowing
{ // Gif::current_frame()
// REFERENCE `self` BORROWS `app.active_gifs.gif_ptr[i]`
let self: &mut Gif = &app.active_gifs.gif_ptr[i];
// REFERENCE `frame` BORROWS `app.active_gifs.gif_ptr[i].decoder (boxed).current_frame`
let frame: &Frame = { // Decoder::read_next_frame()
// REFERENCE `self (2)` BORROWS `app.active_gifs.gif_ptr[i].decoder (boxed)`
let self: &mut Decoder = &mut *(*self).decoder;
(*self).current_frame = (*self).get_cur_frame();
return &(*self).current_frame;
}
// REFERENCE `self (2)` IS DROPPED
// REFERENCE `cur_frame` BORROWS `app.active_gifs.gif_ptr[i] (boxed).current_frame`
cur_frame = frame;
}
// REFERENCE `frame` IS DROPPED
// REFERENCE `self` IS DROPPED
match cur_frame {
Some(f) => { do_something_with_frame(f); }
None => { app.active_gifs.remove(i); }
} else {
// REFERENCE `cur_frame` IS DROPPED
}
}
Where did I get it wrong?
Also, for the second compiler error, moving the remove()
method out of the else, as shown below, does not work, and that made me question my existence.
for i in 0..app.active_gifs.len() {
if let Some(frame) = app.active_gifs[i].current_frame() {
println!("Frame buffer: {:?}", frame.buffer);
return;
}
app.active_gifs.remove(i);
}
EDIT: The two fields (decoder
, the owned value, and current_frame
, the reference) are in the same struct because, if they were not, I would need to loop through both of them. In each iteration, I may need to update current_frame
with a new &Frame
, returned by the decoder
field when the time between frames passes.