这个问号运算符是什么意思?


Answers:


144

您可能已经注意到,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上面的陈述。简而言之:Result如果确定可以解压缩,否则返回错误。

这有点魔术,但是错误处理需要一些魔术来削减样板,并且与异常不同,它可以立即看到哪些函数调用可能会出错,也可能不会出错:那些装饰有?

魔术的一个例子是,它也适用于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`
}

这是由(不稳定)Try特征支持的。

也可以看看:


5
如果可以将答案扩展一点,那就太好了,例如,讨论该函数的返回类型必须与您尝试“展开”的类型匹配,例如ResultOption
hellow

@hellow我想这最好是一个新问题
Paul Razvan Berg

2

它用于可传播错误类型Result <T,E>的错误传播。它可以包装结果并为您提供内在价值。

您无需处理错误的情况,而是将其传播到调用者代码并仅处理确定的情况。好处是,它消除了很多样板,并使功能的实现更简单。


不要将与.unwrap()发生错误时的实际恐慌相混淆。
乔丹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.