I am trying to understand Rust polymorphism. From my background in OOP, I would expect the following Rust code to work:
use std::io::{stdin, Read};
fn main() {
let r: Read = stdin();
println!("ok");
}
But it doesn't:
4 | let r: Read = stdin();
| ^^^^^^^ expected trait std::io::Read, found struct `std::io::Stdin`
I know that there's a Read impl for StdIn, so how can I make this (or whatever is the correct way to do this) work, ie. use Stdin, or a File, or even a String if possible (couldn't find a implementation for that) be used where a Read is expected?
I don't think I can use generics here as I need to pass an instance of r, which can be whatever that implements Read, to another method later, but please tell me if I am wrong.
You probably want to use trait objects here. You can use traits in basically two ways:
fn foo<T: Trait>(x: T). This reads "for an arbitrary, but fixed T which implements Trait".fn foo(x: &Trait). This lifts the "but fixed" limitation from the first version by using trait objects.If you want to have a variable which could either hold a reference to Stdin or a File or anything else that implements Read, you can't go for the static dispatch solution, as your implementing type is not fixed.
So what is the type of your variable then? Read? Sadly, it's not that easy. Trait objects are unsized and can't be used on the stack directly. Instead you can only interact with trait objects via references/pointers, like &Read, &mut Read, Box<Read> and so on. Now that we use borrowing-stuff, we might encounter more problems related to that. Luckily, you haven't been the first one encountering this issue: see this question for a detailed look at this specific use case.
To simplify a bit, in most cases it's ok to use Box<Trait>. In your case, it would look like this:
use std::io::{stdin, Read};
fn main() {
let r: Box<Read> = Box::new(stdin());
println!("ok");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With