Hey everyone!
I'm trying to implement line reader that doesn't do any allocations using a fixed buffer size.
I don't really understand why borrow checker complains since line
is not retained after the iteration, but was just copied.
It doesn't even work for simple usage like this for some reason:
loop {
if matches!(reader.next_line(), None) {
break;
}
}
I've read all the posts I could find here and stackoverflow on mutable borrow in a loop,
but still can't figure it out. Help is appreciated.
use memchr::memchr;
use std::io::Read;
use std::marker::PhantomData;
/// Reads lines from the stream using pre-allocated buffer.
/// Lines shorter than buffer will be returned as-is, lines longer
/// than buffer will be truncated.
struct LineReader<'a, R: Read> {
input: R,
buffer: &'a mut [u8],
read_start: usize,
read_end: usize,
phantom: PhantomData<&'a str>,
}
impl<'a, R: Read> LineReader<'a, R> {
fn new(input: R, buffer: &'a mut [u8]) -> Self {
LineReader {
input,
buffer,
read_start: 0,
read_end: 0,
phantom: PhantomData,
}
}
fn next_line(&'a mut self) -> Option<&'a [u8]> {
if self.read_start == self.read_end {
let Ok(n) = self.input.read(&mut self.buffer) else {
return None;
};
self.read_start = 0;
self.read_end = n;
}
if let Some(index) = memchr(b'\n', &self.buffer[self.read_start..self.read_end]) {
let slice: &'a [u8] = &self.buffer[self.read_start..index];
self.read_start = index;
dbg!(&self.buffer[self.read_start..index]);
return Some(slice);
} else {
todo!(
"read until \\n from {} {} {:?}",
self.read_start,
self.read_end,
self.buffer
);
}
}
}
#[cfg(test)]
mod tests {
use crate::LineReader;
use std::io::Cursor;
#[test]
fn test_line_reader() {
let mut buffer = [0u8; 10];
let mut reader =
LineReader::new(Cursor::new("01234\n56789\n01234\n5678\n9\n"), &mut buffer);
let mut lines = vec![];
loop {
if let Some(line) = reader.next_line() {
lines.push(String::from_utf8_lossy(line).into_owned())
} else {
break;
}
}
assert_eq!(&["abc".to_owned()], lines.as_slice());
}
}
Errors:
Compiling playground v0.0.1 (/playground)
warning: struct `LineReader` is never constructed
--> src/lib.rs:8:8
|
8 | struct LineReader<'a, R: Read> {
| ^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: associated items `new` and `next_line` are never used
--> src/lib.rs:17:8
|
16 | impl<'a, R: Read> LineReader<'a, R> {
| ----------------------------------- associated items in this implementation
17 | fn new(input: R, buffer: &'a mut [u8]) -> Self {
| ^^^
...
27 | fn next_line(&'a mut self) -> Option<&'a [u8]> {
| ^^^^^^^^^
warning: `playground` (lib) generated 2 warnings
error[E0499]: cannot borrow `reader` as mutable more than once at a time
--> src/lib.rs:67:29
|
67 | if let Some(line) = reader.next_line() {
| ^^^^^^
| |
| `reader` was mutably borrowed here in the previous iteration of the loop
| first borrow used here, in later iteration of loop
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` (lib test) due to 1 previous error