Rust 中的错误处理基本分为 panic 和 Result
panic
非必要不要使用
panic 是线程级别的,即不影响其他线程的使用
panic! 允许接受类似 pringln! 类型的参数
Result
Rust 是没有异常的,如果存在错误,都是使用一个 Result 类型使用的
fn get_weather(location: LatLng) -> Result<WeatherReport,io::Error>
函数返回是 Ok(weather) 或者是 Err(error_value)
对于错误的捕获和处理,可以使用类似 match 的方式
match get_weather(hometown) {
Ok(report) => {
display_weather(hometown, &report);
}
Err(err) => {
println!("error querying the weather: {}", err);
schedule_weather_retry();
}
}
但是这种方式还是很繁琐,因为 Result 内置了一些方法,可以快速处理希望的操作:
- result.is_ok() 和 result.is_err() 返回 bool 值,告诉我们 result 是成功的结果还是错误的结果
- result.ok() 返回 Option 类型的成功值(如果有的话)。如 果 result 是一个成功的结果,就返回 Some(success_value);否 则,返回 None,而丢弃错误值。
- result.err() 返回 Option 类型的错误值(如果有的话)
- result.unwrap_or(fallback) 返回成功值,如果 result 是成功的结果的话。否则,它返回 fallback,丢弃错误值
- result.unwrap_or_else(fallback_fn) 是类似的,只是传入的不 是后备值,而是一个函数或闭包。这个方法适合计算后备值如果 用不上会造成浪费的情况。只有在返回错误结果时才会调用 fallback_fn。
- result.unwrap() 也会返回成功值(如果 result 是成功的结果 的话)。不过,如果 result 是错误的结果,这个方法则会panic
- result.expect(message) 与 .unwrap() 相同,只不过你需要自己 提供panic时打印到控制台的消息
除了 .is_ok() 和 .is_err() 之外,其他方法都会 调用它们的 result 值。换句 话说,它们通过 self 参数得到了 result 的值 而这正是 .as_ref() 和 .as_mut() 派上用场的时候
打印错误
println!("error querying the weather: {}", err);
// 打印详细信息
println!("error: {:?}", err)
err.description() 返回 &str 类型的错误消息 err.cause() 返回一个 Option<&Error>,这是触发 err 的底层错误(如果有的话)
传播错误
如果希望出现错误,直接 return ,沿着栈继续向上传播
let weather = get_weather(hometown)?;
// 等价于
let weather = match get_weather(hometown) {
Ok(success_value) => success_value,
Err(err) => return Err(err)
};
? 操作符的行为取决于这个函数是返回一个成功结果,还是返回一个错误结果。
- 如果是成功结果,那么它会打开 Result 并取出其中的成功值。 这里 weather 的类型不是 Result<WeatherReport, io::Error>, 而是简单的 WeatherReport。
- 如果是错误结果,那么它会立即从闭合函数中返回,将错误结果 沿调用链向上传播。为确保传播成功,只能对返回类型为 Result 的函数使用 ?。
use std::fs;
use std::io;
use std::path::Path;
fn move_all(src: &Path, dst: &Path) -> io::Result<()> {
for entry_result in src.read_dir()? { // 打开dir可能失败
let entry = entry_result?; // 读取dir可能失败
let dst_file = dst.join(entry.file_name());
fs::rename(entry.path(), dst_file)?; // 重命名可能失败 }
Ok(()) // 哇哦! }
处理 ”不会发生“ 的错误
某些情况下,我们就 某个错误不会发生 最好的办法是使用 .unwrap()
let num = digits.parse::<u64>().unwrap();
使用 .unwrap() 或 .expect(message) 来省略 Result 是可以接受的
忽略错误
let _ = writeln!(stderr(), "error: {}", err); // 没问题,忽略结果
main 中处理错误
当错误传递到main 的时候,就不能继续向上传播了
fn main() {
calculate_tides().expect("error"); // 麻烦到此为止
}
如果 calculate_tides() 返回一个错误结果,.expect() 方法则会诧 异。主线程中的诧异会打印错误消息,然后以一个非零退出码退出。
不过错误信息还是打印的太多了
$ tidecalc --planet mercury
thread 'main' panicked at 'error: "moon not found"',
/buildslave/rust-buildbot/s
lave/nightly-dist-rustc-linux/build/src/libcore/result.rs:837
note: Run with `RUST_BACKTRACE=1` for a backtrace.
错误消息混在了一堆文字中间。同样,在这种情况下 RUST_BACKTRACE=1 不是个好主意。最好还是自己来打印错误消息:
fn main() {
if let Err(err) = calculate_tides() {
print_error(&err);
std::process::exit(1);
}
}
$ tidecalc --planet mercury
error: moon not found