Passing a BufReader as an AsMut<BufRead>


#1

Hello,

I’m having quite a bit of trouble with the code below, and after starting at it for a long while, I can’t figure out where my misunderstanding lies.

I have a function that has to extract a “chunk” from a reader and it looks like this:

fn read_chunk<R: AsMut<io::BufRead>>(mut input: R) -> Result<String, Error> {
    ...
}

As I understand it, AsMut<io::BufRead> should let me pass a mutable reference to any input that implements the io::BufRead trait. Right?

And then I’m trying to write a test for this function that looks like this:

let mut reader = io::BufReader::new("".as_bytes());
assert_eq!("", read_chunk(reader).unwrap());

But the call to read_chunk cannot be fulfilled:

error[E0277]: the trait bound `std::io::BufReader<&[u8]>: std::convert::AsMut<(dyn std::io::BufRead + 'static)>` is not satisfied

I’m out of ideas on how to call read_chunk correctly (or if its definition is even correct), and rather than getting things to work by blind attempts, I’d like to truly understand what’s going on. Any pointers please? :slight_smile:

Thanks!


#2

Looking at the implementors of AsMut, it seems to be only usable to get from an owned type T to a mutable reference of it & mut T (and not to its traits).

In the case of read_chunk, it could be defined as follows:

fn read_chunk<R: io::BufRead>(mut input: R) -> Result<String, Error> { ... }

Simply removing the AsMut, as R: io:BufRead actually means "any type R that implements the trait io::BufRead, which is want you want here.

Playground link

Trait bounds in the Rust book


#3

Nice, that worked. Thanks!

The reason I jumped to AsMut from the beginning is because I was trying to mimic what I had previously done in other functions with AsRef<Path> and assumed I had to use a similar pattern here. Couldn’t imagine this simpler way worked, but I’m glad it does. I guess I don’t understand this part of the language well-enough just yet.


#4

That would actually be fn read_chunk<R: io::BufRead>(input: &mut R). @kimsnj’s solution works in all the same situations because of this blanket impl: BufRead is implemented for &mut R where R: BufRead.

You probably used AsRef<Path> to abstract over different ways of holding a Path because Path is a type. But BufRead is a trait, so abstracting over different things that implement BufRead is simpler (abstraction being the whole point of traits).

(AsMut<BufRead> actually means something quite different, using dynamic dispatch, and should really be written AsMut<dyn BufRead> these days. Part of the reason dyn was added was to allow the compiler to give more helpful error messages in cases like yours.)