在 Rust 中为 Option<number> 做 max 的惯用方法

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

我在Option中有一个号码。我需要用它的最大值和其他一些值替换它,或者如果我的号码是 None.

,则使用其他值

我写了一个函数来这样做:

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

但我一直在想,使用 Option 的方法有更好、更简洁的方法来做到这一点。你能帮帮我吗?

使用map:

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

Playground

map_or默认展开的术语:

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

Playground

您实际上不需要返回的 Option:

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

...对于那些函数式编程书呆子...

使用 monad 属性的另一种方法,也许这可以帮助您理解 monad,它允许给定 Thing<A> 应用函数 A -> Thing<B> 以获得 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)
}

Option<T> implements OrdT 实现 Ord 时,您将能够比较 Option<T> 值。

因为std::cmp::max<T: Ord>(...)做比较,所以可以这样实现你的功能:

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

Playground


请注意,对于每个 T: OrdSome<T> 都大于 None,这满足您的要求。

这没有记录在案,但我可以这么说,因为 Ord 类型构成了 total order。这意味着它需要是可传递的;如果 None 小于 Some(MIN),则 None 将小于集合中的任何其他值 (Option<T>),因为 Some(MIN) 小于任何值在集合中,除了 None

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

您还可以通过添加额外的 or 来扩展 std::cmp::min,这也适用于 max 的情况,但不需要这样做,因为会有额外的检查和额外的副本值 b 比较 max.

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

Playground