What is the idiomatic way to map a specific enum variant into Some and others into None?

With these types:

struct Data {
    value: u64,
}

impl Data {
    fn try_clone(&self) -> Result<u64> {
        Ok(Self { value: self.value })
    }
}

enum Thing {
    Foo(Data),
    Bar,
}

Currently I do:

let cloned = if let Thing::Foo(foo) = thing {
        Some(foo)
    } else {
        None
    }
    .and_then(|foo| foo.try_clone().ok())
    .ok_or_else(|| MyError("invalid foo data"))?;

Is there a more idiomatic way to do that? Thanks :wink:

If you want a shorthand, you can add a method to your enum, for example:

impl Thing {
    // NOTE: You might want a `self` or `&mut self` version instead.
    fn as_foo(&self) -> Option<&Data> {
        match self {
            Self::Foo(data) => Some(data),
            _ => None
        }
    }
}

and use it like this:

let cloned = Thing::Foo(foo)
    .as_foo()
    .and_then(|foo| foo.try_clone().ok())
    .ok_or_else(|| MyError("invalid foo data"))?;

Or you could use a macro like the ones in the try_match crate.

1 Like

Thanks for the detailed reply! Unfortunately the enum is from an external crate, so I can't impl it (maybe through an ad-hoc trait but that feels a bit ugly). The crate seems interesting, will try it out :wink:

I think the .and_then callback can be moved just into the first branch:

let cloned = if let Thing::Foo(foo) = thing {
        foo.try_clone().ok()
    } else {
        None
    }.ok_or_else(|| MyError("invalid foo data"))?;
1 Like

Extension traits are a standard pattern. There's nothing ugly about them, they are just regular traits.

Indeed. Maybe not elegant enough? Anyway, here's my attempt:

trait EnumIntoOption {
    type Value;

    fn into_option(self) -> Option<Self::Value>;
}

impl EnumIntoOption for Thing {
    type Value = Data;

    fn into_option(self) -> Option<Self::Value> {
        match self {
            Self::Foo(data) => Some(data),
            _ => None,
        }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.