Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a struct be assigned to a binding with a trait it implements [duplicate]

Tags:

rust

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.

like image 448
Renato Avatar asked May 01 '17 19:05

Renato


1 Answers

You probably want to use trait objects here. You can use traits in basically two ways:

  • The static dispatch way: fn foo<T: Trait>(x: T). This reads "for an arbitrary, but fixed T which implements Trait".
  • The dynamic dispatch way: 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");
}
like image 91
Lukas Kalbertodt Avatar answered Sep 21 '22 01:09

Lukas Kalbertodt