我正在阅读文件的文档:
//..
let mut file = File::create("foo.txt")?;
//..
什么是?在这一行吗?我不记得以前在《Rust Book》里见过。
我正在阅读文件的文档:
//..
let mut file = File::create("foo.txt")?;
//..
什么是?在这一行吗?我不记得以前在《Rust Book》里见过。
当前回答
它用于传播错误。有时我们编写的代码可能会失败,但我们不希望立即捕获和处理错误。如果您有太多的代码来处理每个地方的错误,那么您的代码将不可读。相反,如果发生错误,我们可能希望让调用者处理它。我们希望错误向上传播到调用堆栈。
// 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的情况下,打开一个值并提前返回:
其他回答
现有的答案都很棒!我想给出一个小代码片段来演示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特性提供支持。
参见:
问号运算符吗?相当于试试!宏? 为什么要尝试呢?在不返回选项或结果的函数中使用时不编译?