Idiomatic either-or type

I have a function that is supposed to take either the first argument or the second argument that I am currently implementing via two Option structs:

fn foo(a: Option<Vec<u8>>, b: Option<Vec<u8>>) -> Vec<u8> {
    if a.is_none() && b.is_none() { panic!("Nooo") }
    if a.is_some() && b.is_some() { panic!("nooo") }
    let x = if let Some(val) = a {
        val
    } else {
        b.unwrap()
    };
    x
}

fn main() {
    let x = foo(Some(vec![1, 2]), None);
    println!("x: {:?}", x);
}

However, I would prefer to be able to use a struct that could do something like this:

fn foo(x: Either<Vec<u8>>) -> Vec<u8> 

where x contains either a or b from above. Is there something that is in-built in Rust that can accomplish something like this?

https://docs.rs/either/1.6.1/either/enum.Either.html

2 Likes

either::Either is great because it implements a lot of common traits and has various useful methods.

However, if you don't really need those features, it's also idiomatic to define your own simple enum. Doing this lets you give more descriptive names to the type and its variants. For example:

enum Attachment {
    Compressed(Vec<u8>),
    Uncompressed(Vec<u8>),
}

fn foo(a: Attachment) {
    match a {
        Attachment::Compressed(data) => { ... }
        Attachment::Uncompressed(data) => { ... }
     }
}
2 Likes

Without any additional context, the thing to do here is just define two functions and allow the user to call the one that applies. There's no benefit to using an enum if its sole purpose is to invoke a function, there needs to be some other motivation for creating a data type (such as needing to process/parse multiple options outside of the function.) And then the 'proper' approach is usually dictated by what that other process requires.

1 Like

You can also use renamed imports and a type alias to make Either work with your preferred names, without a newtype.

use either::{Either, Either::Left as Compressed, Either::Right as Uncompressed};

type Compression<'a> = Either<&'a str, &'a str>;

fn report_status(c: Compression) {
    match c {
        Compressed(s) => println!("compressed: {}", s),
        Uncompressed(s) => println!("uncompressed: {}", s),
    }
}

fn main() {
    let status = Uncompressed("wow");
    report_status(status);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ce0952f32ff44b9cab3f80d0d2301e9f

4 Likes