Ok(value) 模式在 for 循环中如何工作?

What how does the Ok(value) pattern work in for loops?

我不明白以下模式的工作原理:

for Ok(value) in do_thing_get_results(){
    use_thing(value)
}

循环会忽略 Err 结果吗?它会简单地 .unwrap() 他们并恐慌吗?它是否有另一个用于处理 Err 值的子句,以避免我编写 match?

适当的 MCVE 给出:

fn the_answer() -> Result<u32, ()> {
    Ok(42)
}

fn main() {
    for Ok(foo) in Some(the_answer()) {
        println!("{}", foo);
    }
}
error[E0005]: refutable pattern in `for` loop binding: `Err(_)` not covered
   --> src/main.rs:6:9
    |
6   |     for Ok(foo) in Some(the_answer()) {
    |         ^^^^^^^ pattern `Err(_)` not covered
    |
    = note: the matched value is of type `Result<u32, ()>`

Patterns used to bind names must be irrefutable, that is, they must guarantee that a name will be extracted in all cases. If you encounter this error you probably need to use a match or if let to deal with the possibility of failure

E0005

您的代码根本无法编译。 Rust 不允许您在 for 循环绑定中使用 refutable patterns

如果需要,您可以通过多种方式查看 Ok 变体。一种是在 循环中使用 if let 语句 。示例:

let res_values = vec![
    Ok(11),
    Ok(22),
    Err(MyError::new("Foo")),
    Ok(33),
];
for r in res_values {
    if let Ok(v) = r {
        println!("An Ok value: {}", v);
    }
}  

输出:

An Ok value: 11
An Ok value: 22
An Ok value: 33

可以看出,在那个例子中我们只是跳过了 Errs。 但是,如果你想处理 Ok 变体 直到你遇到 Err,如果发生这种情况,只需 return 错误你的函数的调用者,你可以这样做:

fn do_stuff() -> Result<(), MyError> {
    let res_values = vec![
        Ok(11),
        Ok(22),
        Err(MyError::new("Foo")),
        Ok(33),
    ];
    for r in res_values {
        println!("An Ok value: {}", r?);
    }
    Ok(())
}

输出:

An Ok value: 11
An Ok value: 22 

这段代码有两点需要注意。

首先,for 循环可以包含模式。来自 the reference, syntax of for loops:

Syntax
IteratorLoopExpression :
  for Pattern in Expressionexcept struct expression BlockExpression

这与 let 语句相同,它们可以在其绑定或函数参数中包含模式。例如。 let _ = ...;_wildcard pattern)或 fn f(Struct(v): Struct) {}(其中 struct Struct(v);)。

但是,与 matchif let 中的模式相反,这些位置中的模式必须是 irrefutable。这意味着模式匹配应该总是成功。

模式 Ok(v) 通常属于枚举 Result<T, E>,其中 Ok 是变体之一。这意味着它是一个 refutable 模式,这意味着它被禁止在 for 循环中使用。所以这段代码应该是编译时错误。

除非...

此代码有两种工作方式:

第一个是 Ok 名字被你自己的名字遮盖了。例如,

pub struct Ok<T>(pub T);

fn foo(v: Vec<Ok<i32>>) {
    for Ok(v /* : i32 */) in v { /* ... */ }
}

Playground.

第二种情况是 Result 只有一种变体。匹配单变量枚举无可辩驳的。

但是Result有两种变体!

有一种特殊的(不稳定的)类型,the never type, denoted !,意思是“不能存在”。如果我们用 ! 填充 Err 变体(这可能是因为 Result 在错误类型上是通用的)...

enum Result<T, !> {
    Ok(T),
    Err(!),
}

...我们得到一个与以下内容几乎相同的枚举:

enum Result<T> {
    Ok(T),
}

即单变体枚举,因为 Err 这种情况永远不存在。

有一个名为 exhaustive_patterns 的不稳定特性,它允许我们将此类情况视为无可辩驳的:

#![feature(never_type, exhaustive_patterns)]

fn foo(v: Vec<Result<i32, !>>) {
    for Ok(v /* : i32 */) in v { /* ... */ }
}

Playground.

(注意:这不仅适用于never类型,也适用于任何其他无误类型,例如std::convert::Infallible)。