How to store one of two constants in a value, where the constants share traits?

Viewed 53

Depending on configuration I need to select either stdout or sink once, and pass the results as an output destination for subsequent output call.s

My Java and C++ experience tell me that abstracting away from the concrete type is wise and makes room for future design changes. This code however won't compile:

let out = if std::env::var("LOG").is_ok() {
    std::io::stdout()
} else {
    std::io::sink()
};

Stating...

`if` and `else` have incompatible types

What is the Rust-o-matic way of solving this?

2 Answers

Dynamic dispatch using trait objects is probably what you need:

use std::io::{self, Write};
use std::env;

fn get_output() -> Box<dyn Write> {
    if env::var("LOG").is_ok() {
        Box::new(io::stdout())
    } else {
        Box::new(io::sink())
    }
}

let out = get_output();

The approach from Peter's answer is probably what you need, but it does require an extra allocation. (Which probably doesn't matter in the least in this case, but could matter in other scenarios.) If you are only passing out downward, i.e. as argument to functions, you can avoid the allocation by using two variables to store the different outputs:

let (mut stdout, mut sink);
let out: &mut dyn Write = if std::env::var("LOG").is_ok() {
    stdout = std::io::stdout();
    &mut stdout
} else {
    sink = std::io::sink();
    &mut sink
};
// ...proceed to use out...
Related