We have std::io::BufReader and std::io::BufWriter in the std. However, they cannot be easily composed to create a thing similar to BufWriter<BufReader<T>> for some arbitrary T: Read + Write, because BufReader<T> does not implement Write and BufWriter<T> does not implement Read. Further more, we cannot create a BufWriter<&mut T> and a BufReader<&mut T> at the same time since this violates the borrow check rules.
There are a few types in the std such as File and TcpStream that can be read and written through buffered readers and writers at the same time. Since &File and &TcpStream implement both Read and Write, we can create BufReader<&File> and BufWriter<&File> at the same time without violating borrow check rules. This approach introduces unnecessary unsafe code in certain circumstances, though. For example, if I want to put a BufReader<&File> and a BufWriter<&File>, which read and write into the same underlying File, into a single struct:
struct Foo {
reader: BufReader<&'static File>,
writer: BufWriter<&'static File>,
}
impl Foo {
fn new(f: File) -> Self {
let boxed_file = Box::leak(Box::new(f));
Self {
reader: BufReader::new(boxed_file),
writer: BufReader::new(boxed_file),
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
self.writer.flush().unwrap();
let boxed_file = *self.reader.get_ref();
unsafe {
Box::from_raw(boxed_file as *const File as *mut File);
}
}
}
I come up with 2 approaches to support buffered reader and writer:
First, we can add a new struct named BufRw or similar to the std, which behaves exactly as the simple combination of BufReader and BufWriter:
pub struct BufRw<T: Read + Write> { /* fields omitted */ }
impl<T: Read + Write> Read for BufRw<T> { /* omitted */ }
impl<T: Read + Write> Write for BufRw<T> { /* omitted */ }
impl<T: Read + Write> BufRead for BufRw<T> { /* omitted */ }
impl<T: Read + Write> Seek for BufRw<T> { /* omitted */ }
Second, we can let BufReader and BufWriter implement Write and Read, correspondingly:
impl<R: Write> Write for BufReader<R> { /* omitted */ }
impl<W: Read + Write> Read for BufWriter<W> { /* omitted */ }
So that BufReader and BufWriter can compose.
Do you think that supporting buffered reader + writer in std is a good idea? If so, which approach do you prefer?