Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a variable that conforms to a trait in Rust? [duplicate]

Tags:

rust

traits

I want to instantiate a variable that I will pass into a function that takes a T: Write. It would be defined as follows:

let outputFile = match matches.opt_str("o") {
    Some(fileName) => File::create(fileName).expect("could not open output file"),
    None => std::io::stdout()
};

Currently, the compiler will complain about non-matching types in arms (File vs. Stdout). All I want is to declare outputFile as something I can call any method from the Write trait on and nothing else.

Does Rust allow me to do this or do I have to bring this entire match expression as a parameter to be passed into that function?

like image 882
Matoe Avatar asked Mar 10 '23 00:03

Matoe


1 Answers

If you want to return one of two (or more) separate types that implement a trait, then you need to return a trait object.

In this case you need the return value to own the object (since otherwise the File would be destroyed before the end of the match), so it it makes sense to use Box<dyn Write>. Trait objects such as &Write and Box<dyn Write> are "fat" pointers, which include both a pointer to the struct (File or Stdout in this case) as well as a pointer to a vtable which describes how to implement Write. Importantly, Box<dyn Write> and &Write automatically implement Write.

Here's a working version (playground):

fn get_writer(f: Option<&str>) -> Box<dyn Write> {
    match f {
        Some(file_name) => Box::new(File::create(file_name).expect("could not open output file")),
        None => Box::new(std::io::stdout()),
    }
}

I've made a few changes from your code:

  • Added the Box<dyn Write> return value (without a function you may need to have let outputFile: Box<dyn Write> = ...; without having the type defined somewhere the compiler won't be able to infer that it needs to coerce the two types to the common Box<dyn Write>. Once the compiler knows that it needs Box<dyn Write>, it can coerce a Box<File> to Box<dyn Write>.

  • Boxed the two results.

  • Renamed fileName to file_name to match Rust conventions (and silence the warning).

like image 124
Chris Emerson Avatar answered Apr 28 '23 13:04

Chris Emerson