当 Option 为 None 或内部值满足某些条件时,执行某些操作的惯用方法是什么?

thi*_*ndy 30 rust

有没有更惯用的方式来表达如下内容?

fn main() {
    let mut foo: Option<u8> = None;
    match foo {
        Some(foo_val) if ! (foo_val < 5) /* i.e. the negation of my acceptance condition */ => {}
        _ => { foo.replace(5); }
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎大多数时候都有一种替代方案可以替代一只不做任何事情的手臂,但我一直无法找到适合这种特殊情况的替代方案。

我想说的是更直接的if foo.is_none() || /* some way to extract and test the inner value */ { ... },或者也许是一些我无法理解的链接技巧。

Luk*_*odt 56

//        in None case\n//             \xe2\x94\x82       in Some(_) case\n//            \xe2\x94\x8c\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x90  \xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90    \nif foo.map_or(true, |foo_val| foo_val < 5) {\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

有关详细信息,请参阅Option::map_or

\n

  • 可能更具可读性:`foo.map(|foo_val| foo_val &lt; 5).unwrap_or(true)` (15认同)
  • 一百次?我不确定,一旦你知道 `Option&lt;T&gt;` 是什么,这个方法应该做什么似乎相当直观。作为练习,我曾经在 C# 中实现了一个“Option”类,并在不知道 Rust 中存在的情况下添加了此方法,这是有意义的。 (6认同)
  • 我已经使用 Rust 有一段时间了,如果我在野外遇到它,我怀疑我是否能理解它。(看看其他答案,我怀疑没有真正惯用的方法来做到这一点......) (4认同)
  • @Caesar 凡事都有第一次。如果我们只编写最广泛的受众可以逐行理解的代码,那么所有代码​​都将是臃肿、缺乏表达力的代码:一堵文本墙,人们可以理解每一行在做什么,但必须努力弄清楚大局(它实际上想要实现的目标)。编写的代码应该对目标受众有意义——对从事代码工作的团队来说,或者如果是图书馆,对在其所涵盖的领域有一定经验的人来说是有意义的。默认情况下,它不应该针对所有背景的一般程序员受众。 (3认同)

at5*_*321 28

有很多方法可以做到这一点。最简单的(也可以说是最易读的)之一是这样的:

if foo.unwrap_or(0) < 5 {
    ...
}
Run Code Online (Sandbox Code Playgroud)

上述两种情况都成立:

  • 当的值小于foo时;Some5
  • foo什么时候None

在一些更复杂的场景中,需要计算“默认”值并且性能至关重要,您可能需要考虑unwrap_or_else.

正如卢卡斯所建议的,该map_or方法也可以使用。请注意,传递给的参数map_or是急切求值的,因此如果性能至关重要,您可能需要考虑map_or_else作为替代方案。

铁锈 1.70+

is_some_and方法已在 Rust 1.70 中稳定下来。这是一个例子:

if name.is_some_and(|s| s.len() > 50) {
    println!("This is a long name!");
}
Run Code Online (Sandbox Code Playgroud)

在许多情况下,它应该比map_or/更具可读性map_or_else


Jmb*_*Jmb 15

您可以使用filter(使用条件的否定)和is_none

if foo.filter(|&x| !(x < 5)).is_none() {
    // Here either foo was None or it contained a value less than 5
}
Run Code Online (Sandbox Code Playgroud)


Max*_*axV 6

我不确定我完全理解你的问题,但你可以尝试这样的事情:

fn main() {
    let foo: Option<u8> = None;
    let result = foo.filter(|foo_val| !(*foo_val < 5) ).unwrap_or(5);
    println!("Result: {result}");
}
Run Code Online (Sandbox Code Playgroud)

Playground上的更多示例


小智 6

比赛宏似乎很合适:

if matches!(foo, Some(a) if a>=5) { foo.replace(5) }
Run Code Online (Sandbox Code Playgroud)

铁锈游乐场


Luk*_*odt 3

从 Rust 1.70.0 (2023-06-02) 开始,Option::is_some_and可以使用。它是为相反的用例构建的,如问题所示:false如果是,则返回None

if foo.is_some_and(|foo_val| foo_val < 5) { 
    // ...
}
Run Code Online (Sandbox Code Playgroud)