您可能已经注意到,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
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
特性提供支持。
另请参阅:
它是一个后缀运算符,用于解包
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 提供的便利,消除了样板代码并使函数的实现更简单。
它用于
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.
?
的行为取决于该函数返回成功结果还是错误结果:
file
file
。函数将错误返回给调用者使用
?
与此代码相同
let mut file = match File::create("foo.txt") {
Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
?
也与选项类型类似。在返回 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)),
}
}