我正在阅读文件的文档:

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

什么是?在这一行吗?我不记得以前在《Rust Book》里见过。


当前回答

现有的答案都很棒!我想给出一个小代码片段来演示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)),
    }
}

其他回答

现有的答案都很棒!我想给出一个小代码片段来演示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)),
    }
}

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

 // 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

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

如果它是一个成功,它将展开Result以获得其中的success值。值赋给变量文件 如果结果为错误,则错误不会赋值给变量文件。错误由函数返回给调用者

使用?和这段代码一样

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

? 同样适用于Option类型。在返回Option的函数中,您 能用吗?在为None的情况下,打开一个值并提前返回:

它是一个后缀操作符,用于展开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

的吗?operator只能用于返回Result或Option的函数,如下所示:

use std::num::ParseIntError;

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

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

正如您可能已经注意到的,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`
}

这样更简洁。

什么?Does在这里等价于上面的match语句加上一个加法。简而言之:

如果OK,它将解包Result 如果没有,则返回错误,在错误值上调用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特性提供支持。

参见:

问号运算符吗?相当于试试!宏? 为什么要尝试呢?在不返回选项或结果的函数中使用时不编译?