【问题标题】:What is this question mark operator about?这个问号运算符是关于什么的?
【发布时间】:2017-03-21 02:50:31
【问题描述】:

我正在阅读the documentation for File

//..
let mut file = File::create("foo.txt")?;
//..

这一行中的? 是什么?我不记得以前在 Rust Book 中看到过。

【问题讨论】:

标签: rust


【解决方案1】:

您可能已经注意到,Rust 没有例外。它有恐慌,但不鼓励将它们用于错误处理(它们用于不可恢复的错误)。

在 Rust 中,错误处理使用 Result。一个典型的例子是:

fn halves_if_even(i: i32) -> Result<i32, Error> {
    if i % 2 == 0 {
        Ok(i / 2)
    } else {
        Err(/* something */)
    }
}

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = match halves_if_even(i) {
        Ok(i) => i,
        Err(e) => return Err(e),
    };

    // use `i`
}

这很棒,因为:

  • 在编写代码时不能不小心忘记处理错误,
  • 阅读代码时,您可以立即发现此处可能存在错误。

然而,它并不理想,因为它非常冗长。这就是问号运算符? 的用武之地。

上面可以改写为:

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = halves_if_even(i)?;

    // use `i`
}

这样更简洁。

? 在这里所做的等价于上面的 match 语句加上一个附加。简而言之:

  1. 如果正常,它会解压Result
  2. 如果没有,它返回错误,并在错误值上调用Into::into 以潜在地将其转换为另一种类型。

这有点神奇,但是错误处理需要一些魔法来减少样板文件,并且与异常不同的是,它可以立即看到哪些函数调用可能会或可能不会出错:那些以? 装饰的函数调用。

一个神奇的例子是这也适用于Option

// Assume
// fn halves_if_even(i: i32) -> Option<i32>

fn do_the_thing(i: i32) -> Option<i32> {
    let i = halves_if_even(i)?;

    // use `i`
}

? 运算符 stabilized in Rust version 1.13.0 由(不稳定的)Try 特征提供支持。

另见:

【讨论】:

  • 如果你能稍微扩展你的答案会很好,例如讨论函数的返回类型必须与您尝试“解包”的类型匹配,例如ResultOption.
  • @hellow 我想这最好是一个新问题
  • 关于带有结构化信息的恐慌,他们现在可以感谢panic_any
  • @Lonami:适当修改。
【解决方案2】:

用于可恢复错误类型Result&lt;T, E&gt; 的错误传播。它解开结果并为您提供内部价值。

您不是处理错误情况,而是将其传播到调用者代码并仅处理Ok 情况。好处是,它消除了很多样板,使函数的实现更简单。

【讨论】:

  • 不要与实际的.unwrap() 混淆,后者在发生错误时会出现恐慌。
猜你喜欢
  • 2012-02-13
  • 2021-07-12
  • 2012-05-07
  • 2016-03-07
  • 1970-01-01
  • 2014-03-04
  • 2012-10-29
相关资源
最近更新 更多