Search code examples
enumsrustpattern-matchingmonadscombinators

Composing Option<Enum> with closures that pattern-match arguments


I have an enum with two different possible "types" and a function that may return any of them, wrapped in an Option:

enum Possibilities {
    First(i32),
    Second(String),
}
use Possibilities::*;

fn some_poss() -> Option<Possibilities> {
    Some(Second(String::from("hi")))
}

I want to apply an operation to the result of some_poss, but this operation only makes sense for one of the possibilities of the enum, otherwise it should return None. For example:

let a: Option<i32> = some_poss().and_then(|poss| if let First(x) = poss {
    Some(x * 2)
} else {
    None
});

How can I concisely combine this operation? Would it be possible to write this in a way similar to the following?

// Compile error: pattern `Second(_)` not covered
let b: Option<i32> = some_poss().map(|First(x)| x * 2);

Solution

  • The best way to handle this specific case would be to create a method on the enum specifically for retrieving the one variant. Somewhat like Result::ok.

    enum Possibilities {
        First(i32),
        Second(String),
    }
    use Possibilities::*;
    
    impl Possibilities {
        fn first(self) -> Option<i32> {
            match self {
                Possibilities::First(x) => Some(x),
                _ => None,
            }
        }
    }
    

    This would allow you to implement your function like:

    some_fun().and_then(|p| p.first()).map(|x| x * 2)
    // or, if you prefer this style:
    some_fun().and_then(Possibilities::first).map(|x| x * 2);
    

    This makes it explicit what each step is doing - some_poss gets an Option<Possiblities>, then first() gets an Option<i32> from that Possibilities, and then and_then collapses Option<Option<i32>> into Option<i32>.