Read is BufRead or not?

I have a function like this:

fn do_something(r: &mut Read) { ... }

Inside that function I need buffering to avoid small reads.

Currently I can do it like that:

let br = BufReader::new(r.by_ref());
work with br

In case r is already buffered I'm getting double buffering here.

So I need to wrap r in BufReader only if r is not yet buffered.

Is there any way to test if Read is buffered and cast it to BufRead?

My understanding is that trait Read lacks a function like that:

trait Read {
    ...
    // if `self` is `BufRead`, cast `self` to `BufRead`
    fn as_buffered(&mut self) -> Option<&mut BufRead> { None }
}

so I could write my code like this:

use std::io::*;

fn buffered(_r: &mut Read) -> Option<&mut BufRead> {
    // is it possible to implement it without patching rust std?
    ... cast or return None
}

fn do_something(read: &mut Read) {
    let mut buf_reader_holder;
    let buf_read;
    if buffered(read).is_some() {
        buf_read = buffered(read).unwrap();
    } else {
        buf_reader_holder = Some(BufReader::new(read));
        buf_read = buf_reader_holder.as_mut().unwrap();
    }
    // do the reading
    buf_read.lines();
}

fn main() {
    my_func(&mut stdin());
}

Moreover Read trait could provide a function like:

trait Read {
    ...
    // Buffered adapter does own buffering only if `self` is not already buffered
    fn buffered(&mut Stream) -> BufferedReadAdapter { ... }
}

Why can't I just take BufRead in the signature?

  • It exposes function internals
  • Sometimes it is not statically known whether Read is buffered or not, e. g. a function like open_http_get_stream(url: &str) -> HttpStreamReader returns a stream that is buffered for SSL or zipped connection and not buffered otherwize.
1 Like

If you need a BufRead, take a BufRead. If the user only has a Read then leave it up to them to pass the appropriate adapter.

5 Likes

Since you only borrow the Read, I think it's error prone to temporarily wrap it in a buffer anyway -- there will be unconsumed input stored in the buffer, that gets lost when your function returns.

Creating another layer of a buffer costs allocation & initialization time, but if the sizes of the buffers match up, reading the inner buf reader will actually straight bypass the buffering, so you're not really double buffering. Small illustration of that on the playpen -- it only uses the outer buffer.

4 Likes