Non orthogonal behavior of function

Hi, the below works as expected, that is, I can pass as an arg to that fn any csv reader, be it created from file, stdin etc.
fn bar<R: std::io::Read>(rdr:&mut csv::Reader<R>) {}//works

The below on the other hand doesn't:

fn baz<R: std::io::Read>() ->Box<csv::Reader<R>>{
    let mut rdr = ReaderBuilder::new().from_reader(std::io::stdin());
    Box::new(rdr)
}

the error I'm getting:

Box::new(rdr)
| -------- ^^^ expected Reader<R>, found Reader<Stdin>
| |
| arguments to this function are incorrect
|
= note: expected struct csv::Reader<R>
found struct csv::Reader<std::io::Stdin>

Surely, the R is not an R if you know what I mean but std::io::Stdin, that's why I've said that in the <...> brackets.

The key thing to understand here is that the generic arguments (R in this case) are always chosen by the caller. In your first example, the function is designed to work with any type that implements Read and therefore compiles.

The second, however, introduces a problem: According to the function signature, the caller should be able to get a Box<Reader<std::io::File>> if that’s what they want. But the function body can only provide a Box<Reader<Stdin>>.

3 Likes

Hi, thank you for your reply and clarifications.
So I've done this:

fn baz() ->csv::Reader<dyn std::io::Read>{
    let mut rdr = ReaderBuilder::new().from_reader(std::io::stdin());
    rdr
}

Also doesn't compile, and yet "it should" :slight_smile:
Any ideas how to fix it?

dyn std::io::Read is a dynamically-sized type (DST for short), it is a type itself, but in order to use DST, it needs a special ?Sized bound. so by itself, the return type is already invalid, let alone the implementation returns a different type.

in your case, since you are returning a single concrete type (std::io::Stdin), you should just spell the type directly.

if, for some reason, you are not willing to specify the actual type, you can use existential type (i.e. impl std::io::Read), but not the trait object (i.e. dyn std::io::Read).

Hi, but this is just an example. I want to be able to return different Readers, that is Readerstd::io::Stdin as well as Reader<&[u8]> etc

oh, I don't know what Reader<&[u8]> is,I assume you mean Cursor<&[u8]>, right? do you want something like this? note dyn SomeTrait is DST, but Box<dyn SomeTrait> is not.

fn baz(use_stdin: bool) ->csv::Reader<Box<dyn std::io::Read>>{
    if use_stdin {
        ReaderBuilder::new().from_reader(Box::new(std::io::stdin()))
    } else {
        ReaderBuilder::new().from_reader(Box::new(Cursor::new(&[12, 34, 56, 78])))
    }
}
4 Likes

Thank you very much for your help. I thought that returning Box<Reader> would do the trick, but obviously it didn't.