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

问题描述 投票:0回答:4

我正在阅读

File
的文档:

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

这行中的

?
是什么?我不记得以前在《Rust Book》中见过它。

rust syntax
4个回答
347
投票

您可能已经注意到,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. 如果OK的话,它会解压
    Result
  2. 返回错误,如果没有,则对错误值调用
    From::from
    以可能将其转换为另一种类型。

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

?
装饰的函数调用。

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

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`
}

?
运算符,在Rust版本1.13.0中稳定由(不稳定的)
Try
特性提供支持。

另请参阅:


27
投票

它是一个后缀运算符,用于解包

Result<T, E>
Option<T>
值。

如果应用于

Result<T, E>
,它会解开结果并为您提供内部值,从而将错误传播到调用函数。

let number = "42".parse::<i32>()?;
println!("{:?}", number); // 42

当应用于

Option<T>
时,它将
None
传播给调用者,让您处理 Some 分支的内容。

let val = Some(42)?;
println!("{:?}", val); // 42

?
运算符只能在返回
Result
Option
的函数中使用,如下所示:

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number = "42".parse::<i32>()?;
    println!("{:?}", number);
    Ok(())
}

这是 Rust 提供的便利,消除了样板代码并使函数的实现更简单。


12
投票

它用于

propagating errors
。有时我们编写的代码可能会失败,但我们不想立即捕获并处理错误。如果您有太多代码来处理每个地方的错误,您的代码将不可读。相反,如果发生错误,我们可能想让调用者处理它。我们希望错误在调用堆栈中传播。

 // file type is Result if "?" is not used
 // file:Result<File,Error>
 let mut file = File::create("foo.txt");

 // file type is File if "?" is used
 // file:File
 let mut file = File::create("foo.txt")?;
 // if an error occurs, code after this line will not be executed
 // function will return the error
 // if we do not return here, the program will continue execution even though an error occurred. This could lead to unexpected behavior or incorrect results.

?
的行为取决于该函数返回成功结果还是错误结果:

  • 如果成功,则解开 Result 来获取里面的成功值。值被分配给变量
    file
  • 如果结果是错误,则该错误不会分配给变量
    file
    。函数将错误返回给调用者

使用

?
与此代码相同

let mut file = match File::create("foo.txt") {
        Err(why) => panic!("couldn't create {}: {}", display, why),
        Ok(file) => file,
    };

?
也与选项类型类似。在返回 Option 的函数中,您 可以使用 ?在 None 的情况下解开一个值并提前返回:


2
投票

现有的答案都很棒!我想给出一个小代码片段来演示这个问号后面的

From::from()
的使用:

fn _parse(str: &str) -> Result<i32, &str> {
    if let Ok(num) = str.parse::<i32>() {
        Ok(num)
    } else {
        Err(str)
    }
}
fn parse(str: &str) -> Result<(), String> {
    let num = _parse(str)?;
    println!("{}", num);
    Ok(())
}

函数

?
parse()
的使用可以手动重写为:

fn parse(str: &str) -> Result<(), String> {
    match _parse(str) {
        Ok(n) => {
            println!("{}", n);
            Ok(())
        }
        Err(str) => Err(<String as From<&str>>::from(str)),
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.