错误处理
Rust 中的两类错误处理:panic 和 Result.
普通错误使用 Result 类型来处理。Result 通常用以表示由程序外部的事物引发的错误,比如错误的输入、网络中断或权限问题。这些问题并不是意料之中的,在没有任何 bug 的程序中也可能会不时出现。
panic 针对的是另一种错误,即那种永远不应该发生的错误。
Panic
当程序遇到下列问题的时候,就可以断定程序自身存在 bug,故而会引发 panic
- 数组越界访问
- 整数除以 0
- 在恰好为 Err 的 Result 上调用 .expect()
- 断言失败。
panic!()
是一种宏,用于处理程序中出现错误的情况。panic
不是崩溃,也不是未定义行为。它更像是 Java 中的 RuntimeException
在 Panic 时也会出现异常,如果 Rust 在试图清理第一个 panic 时,.drop() 方法触发了第二个 panic,那么这个 panic 就是致命的。Rust 会停止展开调用栈并中止整个进程。
Result
例子 1 异常返回
fn get_user_name() -> Result<String, io::Error> {
let mut user_name = String::new();
println!("Enter your name: ");
std::io::stdin().read_line(&mut user_name).unwrap();
let trimmed_name = user_name.trim();
println!("You entered: {}", user_name);
if trimmed_name.eq("Fuck") {
Err(io::Error::new(io::ErrorKind::InvalidInput, "Inappropriate language is not allowed."))
} else {
Ok(user_name.trim().to_string())
}
}
例子 2 捕捉异常 match
fn main() {
let user_name = get_user_name();
match user_name {
Ok(name) => println!("Hello, {}!", name),
Err(e) => println!("Error: {}", e),
}
}
例子 3 Result 别名
//默认情况下
fn get_user_name() -> Result<String,io::Error> {}
//别名情况下
fn get_user_name() -> io::Result<String> {}
例子 4 打印错误
Err(e) => println!("Error: {}", e),
例子 5 传播错误
大多数时候,当我们试图做某些可能失败的事情时,可能不想立即捕获并处理错误。如果在每个可能出错的地方都要使用十来行 match 语句,那代码就太多了。
Rust 的 ? 运算符可以执行此操作。这是一个写在方法中的语法糖.假如是 Main 方法会导致没有返回而异常,也可以直接使用 ?
进行解包.
let weather = get_weather(hometown)?;
let weather = get_weather(hometown).ok()?;
Cannot use the `?` operator in a function that returns `()` [E0277]
例子 6 不可能出现的错误
如果我们正在编写的代码已经返回了 GenericResult,那么就可以添加一个 ?,并且忽略这个错误。否则,我们将不得不为处理不可能发生的错误而烦恼。最好的选择是使用 Result 的 .unwrap() 方法。如果结果是 Err,就会 panic;但如果成功了,则会直接返回 Ok 中的成功值:
let num = digits.parse::().unwrap();
例子 7 忽略错误
有时我们只想完全忽略一个错误。惯用法 let _ = ... 可用来消除这种警告。
let _ = writeln!(stderr(), "error: {}", err); // 正确,忽略结果
例子 8 处理 main() 中的错误
如果你传播错误的距离足够远,那么最终它就会抵达 main(),后者必须对其进行处理。通常来说,main() 不能使用 ?,因为它的返回类型不是 Result:
处理 main() 中错误的最简单方式是使用 .expect():
fn main() {
calculate_tides().expect("error"); // 责任止于此
}
fn main() -> Result<(), TideCalcError> {
let tides = calculate_tides()?;
print_tides(tides);
Ok(())
}
例子 9 声明自定义错误类型
假设你正在编写一个新的 JSON 解析器,并且希望它有自己的错误类型。
return Err(JsonError {
message: "expected ']' at end of array".to_string(),
line: current_line,
column: current_column
});
use std::fmt;
// 错误应该是可打印的
impl fmt::Display for JsonError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{} ({}:{})", self.message, self.line, self.column)
}
}
// 错误应该实现std::error::Error特型,但使用Error各个方法的默认定义就够了
impl std::error::Error for JsonError { }
评论