I've hit some compiler error that does seem like a limitation of the borrow checker to me. But maybe I am overlooking something. I would be happy if someone could have a look at it.
I've reduced my specific case to the following code (Playground):
use std::collections::VecDeque;
struct BufferQueue {
queue: VecDeque<Vec<u8>>,
index: usize,
}
impl BufferQueue {
fn advance_buffer(&mut self) {
self.queue.pop_front();
self.index = 0;
}
fn first_buffer_with_data(&mut self) -> Option<&[u8]> {
while let Some(buffer) = self.queue.front() {
if self.index < buffer.len() {
return Some(&buffer[self.index..]);
} else {
self.advance_buffer();
}
}
None
}
}
If I compile this with the currently stable compiler or with the nightly 2024 edition, I get the following error:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/lib.rs:19:17
|
14 | fn first_buffer_with_data(&mut self) -> Option<&[u8]> {
| - let's call the lifetime of this reference `'1`
15 | while let Some(buffer) = self.queue.front() {
| ---------- immutable borrow occurs here
16 | if self.index < buffer.len() {
17 | return Some(&buffer[self.index..]);
| --------------------------- returning this value requires that `self.queue` is borrowed for `'1`
18 | } else {
19 | self.advance_buffer();
| ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
In my head, this should be fine. The first branch in the loop uses the borrowed buffer, but is returning and therefore should not make any issues. And the second branch does not need the buffer. Therefore, with non-lexical lifetimes, it should not be a problem to again borrow self mutable. But the compiler does not seem to share my point of view
The thing is, I can modify the code more or less slightly and the compiler accepts it (Playground):
fn first_buffer_with_data(&mut self) -> Option<&[u8]> {
while let Some(buffer) = self.queue.front() {
if self.index < buffer.len() {
break; // CHANGED
} else {
self.advance_buffer();
}
}
self.queue.front().map(|buffer| &buffer[self.index..]) // CHANGED
}
Why is that different (except slightly less efficient) than the version before?