有没有更简单的方法从 Rust 的选项中提取值

mar*_*nio 11 rust

我发现自己经常做类似以下的事情:

fn foo() -> Result<i32, String> {
    let cur = match something_that_returns_an_option() {
        Some(cur) => cur,
        None => return Err("Some error"),
    };
    
    // use `cur`
    
    1
}
Run Code Online (Sandbox Code Playgroud)

如果我需要多个变量,我会一遍又一遍地保留此模式,或者如果让/匹配则嵌套。

是否有一种更符合人体工程学的方法来处理从选项中重复提取值?

Aid*_*en4 11

您正在寻找Option::ok_or. 它允许您将 an 映射OptionResult具有提供的错误的 a 。与?操作员结合使用,你可以很好地清理事情:

fn foo() -> Result<i32, String> {
    let cur = something_that_returns_an_option().ok_or("some error")?;
        
    Ok(cur + 1)
}

Run Code Online (Sandbox Code Playgroud)

操场

Option::ok_or_else也可能有帮助,因为它会惰性地评估错误分支。


at5*_*321 7

在您的示例中,您不仅希望继续、中断或返回常规值,还希望返回错误。对于这种特殊情况,Aiden4 的答案就是正确的选择。或者,如果您无论如何都在使用,您应该看到这个

但我曾经遇到过这样的情况:我想None直接解开 或 (在 的情况下)continue或非错误值。breakreturn

更新(Rust 1.65+)

让其他

let -else功能在 Rust 1.65 中得到了稳定。以下是如何使用它的示例:

let options = vec![Some(74), None, Some(9)];
for o in options {
    let Some(v) = o else { continue };
    println!("v = {v}");
}
Run Code Online (Sandbox Code Playgroud)

较旧的答案(以及替代解决方案)

Rust(仍然)没有提供一种简短的方法来做到这一点。

这是一个“one-liner”,可以解决问题,但仍然有点冗长:

let v = if let Some(d) = some_option_value { d } else { continue; };
Run Code Online (Sandbox Code Playgroud)

如果您想要更短的解决方案,请考虑以下事项......

一个宏

你可以写一个这样的宏:

macro_rules! unwrap_or {
    ($e:expr, $or_do_what:expr) => {
        if let Some(d) = $e { d } else { $or_do_what }
    };
}
Run Code Online (Sandbox Code Playgroud)

这将允许您编写如下代码:

let options = vec![Some(74), None, Some(9)];
for o in options {
    let v = unwrap_or!(o, continue);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这是一个微不足道的例子,但我认为如果您需要执行多次检查,那么最大的好处就会到来,这样就不用编写这样的“惯用”内容:

for thing in things {
    if let Some(a) = thing {
        // ...
        if let Some(b) = a.another_opt {
            // ...
            if let Some(c) = a.yet_another_opt {
                // ...
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

,您可以通过避免嵌套多个块来简化代码,如下所示:

for thing in things {
    let a = unwrap_or!(thing, continue);
    // ...
    let b = unwrap_or!(a.another_opt, continue);
    // ...
    let c = unwrap_or!(a.yet_another_opt, continue);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

当然,这是否是一个好的做法是主观的。