Still on my learning journey, I wanted to see how some bits of Rust’s I/O machinery work. From the documentation, I understand that one should use std::io::BufRead
for buffered I/O, so I had a go. From what I understand, you’re supposed to call fill_buf()
to ensure that the buffer has data, and you get a slice of an internal buffer from the call. After processing some bytes from this buffer, you’re supposed to call consume()
to inform the BufRead
that you’re done with those bytes, so you don’t see them again. Both of those are mutating operations.
So I came up with this:
use std::io::{self,BufRead};
pub enum ProtocolError {
InvalidBytes(Vec<u8>),
Io(io::Error),
}
pub struct Processor<B: BufRead> {
reader: B
}
pub enum ProcessResult {
Done,
Error(ProtocolError),
ProtocolValue(u32)
}
impl<B: BufRead> Processor<B> {
pub fn new(r : B) -> Self {
Self { reader: r }
}
pub fn process(&mut self) -> ProcessResult {
let mut pos: usize = 0;
let mut process_result: u32 = 0;
loop {
let reader = &mut self.reader;
let r = reader.fill_buf();
let length: usize;
let buf : &[u8];
match r {
Ok(buffer) => {
buf = buffer;
length = buffer.len();
if length == 0 {
return ProcessResult::Done;
}
},
Err(e) => {
return ProcessResult::Error(ProtocolError::Io(e));
}
}
let mut failed = false;
while pos < length {
let byte = buf[pos];
process_result += (byte as u32) * 2 + 3;
if process_result < 128 || process_result > 256 {
failed = true;
break;
}
pos += 1;
}
reader.consume(pos);
if failed {
let v = buf[0..pos].to_vec();
ProcessResult::Error(ProtocolError::InvalidBytes(v));
} else {
ProcessResult::ProtocolValue(process_result);
}
}
}
}
Of course, there are errors:
Compiling playground v0.0.1 (file:///playground)
error[E0499]: cannot borrow `*reader` as mutable more than once at a time
--> src/lib.rs:56:13
|
29 | let r = reader.fill_buf();
| ------ first mutable borrow occurs here
...
56 | reader.consume(pos);
| ^^^^^^ second mutable borrow occurs here
...
63 | }
| - first borrow ends here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
I want to do two mutating operations in a single block, and the two operations fill_buf()
and consume()
are supposed to be used together. How, exactly? Is it just something dumb I’ve done with one of the declarations?
loop {
let reader = &mut self.reader;
let r = reader.fill_buf();
...
reader.consume();
}