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