I am trying to implement a file lines reader that would not do so many checks and allocations to speed up some file reading.
This should be valid, but the compiler is complaining about borrowing self.buffer as mutable while it is still borrowed as immutable at the beginning of the read_line method. The problem seems to be related to the lifetimes, but I can't explain to the compiler that the first borrow is dropped and has nothing to do with the None => arm anymore.
struct FastFileLinesReader<B: BufRead> {
stream: B,
buffer: Vec<u8>,
cursor: usize,
}
impl<B: BufRead> FastFileLinesReader<B> {
fn new(stream: B) -> Self {
Self {
stream,
buffer: vec![1024],
cursor: 0,
}
}
fn read_line(&mut self) -> Option<&[u8]> {
let line = {
let remainder = &self.buffer[self.cursor..];
remainder.split_once(|v| *v == b'\n' as u8)
};
match line {
Some((line, _)) => {
self.cursor += line.len() + 1;
return Some(line);
}
None => {
self.buffer.copy_within(self.cursor.., 0);
self.cursor = 0;
let n = self.stream.read(&mut self.buffer[self.cursor..]).ok()?;
if n == 0 {
return None;
}
return self.read_line();
}
};
}
}
The error:
error[E0502]: cannot borrow `self.buffer` as mutable because it is also borrowed as immutable
--> src/bin/main.rs:110:17
|
99 | fn read_line(&mut self) -> Option<&[u8]> {
| - let's call the lifetime of this reference `'1`
100 | let line = {
101 | let remainder = &self.buffer[self.cursor..];
| ----------- immutable borrow occurs here
...
107 | return Some(line);
| ---------- returning this value requires that `self.buffer` is borrowed for `'1`
...
110 | self.buffer.copy_within(self.cursor.., 0);
@isaactfa was right that this case is a known limitation of the borrow checker that incorrectly thinks an early-return overlaps with the rest of the function.
In this case though, you can side-step the problem by avoiding references you get from
.split_onceand just use indexes instead: