Idiomatic way to do max for Option<number> in Rust

Viewed 820

I have a number in Option. I need to replace it with max value of it and some other value, or use that other value if my number is None.

I wrote a function to do so:

fn max(a: Option<u32>, b: u32) -> Option<u32> {
    if a.is_some() {
        Some(std::cmp::max(a.unwrap(), b))
    } else {
        Some(b)
    }
}

But I can't stop thinking there is a better and concise way to do so using methods of Option. Can you help me?

3 Answers

Use map:

fn max(a: Option<u32>, b: u32) -> Option<u32> {
    a.map(|v| std::cmp::max(v, b))
}

Playground

Or map_or to default the unwraped term:

fn max(a: Option<u32>, b: u32) -> Option<u32> {
    a.map_or(Some(b), |v| Some(std::cmp::max(v, b)))
}

Playground

For which you actually do not need the returned Option:

fn max(a: Option<u32>, b: u32) -> u32 {
    a.map_or(b, |v| std::cmp::max(v, b))
}

Option<T> implements Ord when T implements Ord, then you'll be able to compare Option<T> values.

Since std::cmp::max<T: Ord>(...) does comparing, it is possible to implement your function like this:

fn max(a: Option<u32>, b: u32) -> Option<u32> {
    std::cmp::max(a, Some(b))
}

Playground


Please note that for every T: Ord, Some<T> is greater than None, which satisfies your requirement.

This is not documented but I can say this because Ord types form a total order. This means it needs to be transitive; if None is lesser than the Some(MIN) then None will be lesser than any other value in the set(Option<T>), because Some(MIN) is lesser than any value in the set except None

assert!(Some(std::i64::MIN) > None);

You can also extend std::cmp::min by adding extra or, this would also work for the max case but it is not needed since there will be additional check and extra copy of the value b comparing the solution for max.

fn min(a: Option<u32>, b: u32) -> Option<u32> {
    std::cmp::min(a.or(Some(b)), Some(b))
}

Playground

...for those functional programming nerds...

Just another way to do it using monad properties, maybe this helps you to understand monads, it allows that given a Thing<A> apply a function A -> Thing<B> to get a Thing<B>:

and_then :: Thing<A> -> (A -> Thing<B>) -> Thing<B>
fn max(a: Option<u32>, b: u32) -> u32 {
    a.and_then(|v| Some(std::cmp::max(v, b))).unwrap_or(b)
}
Related