错误处理

1 Result 处理可恢复的错误

Result枚举类有以下两个成员:

例1.1:

// T, E都是范型类参数
enum Result<T, E> {
Ok(T),
Err(E),
}

以打开文件为例

例1.2:

use std::fs::File;

fn main() {
let greeting_file_result = File::open("hello.txt");

let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {error:?}"),
};
}

File::open的返回值是Result<T, E>,泛型参数T会被File::open的实现放入成功返回值的类型std::fs::File,这是一个文件句柄。错误返回值使用的E的类型是std::io::Error

File::open成功时,greeting_file_result变量将会是一个包含文件句柄的Ok实例。当失败时,greeting_file_result变量将会是一个包含了更多关于发生了何种错误的信息的Err实例。

$ cargo run
thread 'main' panicked at src/main.rs:8:23:
Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

匹配不同的错误

例1.3:

use std::fs::File;
use std::io::ErrorKind;

fn main() {
let greeting_file_result = File::open("hello.txt");

let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {e:?}"),
},
other_error => {
panic!("Problem opening the file: {other_error:?}");
}
},
};
}

2 unwrap 和 expect 等

unwrap()

  • 适用于Option<T>Result<T, E>
  • 直接获取SomeOk内部的值。
  • 如果是NoneErr,程序会直接panic

例2.1:

let x: Result<i32, &str> = Ok(20);
println!("{}", x.unwrap()); // 输出 20

let y: Result<i32, &str> = Err("error");
println!("{}", y.unwrap()); // panic: called `Result::unwrap()` on an `Err` value: "error"

unwrap_err()

  • 仅适用于Result<T, E>
  • 返回Err内部的错误值。
  • Ok,程序会直接panic

例2.2:

let x: Result<i32, &str> = Err("error");
println!("{}", x.unwrap_err()); // 输出 "error"

let y: Result<i32, &str> = Ok(10);
println!("{}", y.unwrap_err()); // panic: called `Result::unwrap_err()` on an `Ok` value: 10

expect(msg)

  • 适用于Option<T>Result<T, E>
  • 作用类似于unwrap(),但当panic时,会输出自定义的错误信息,便于调试。

例2.3:

let x: Result<i32, &str> = Ok(20);
println!("{}", x.expect("Result was Err")); // 输出 20

let y: Result<i32, &str> = Err("error");
println!("{}", y.expect("Result was Err")); // panic: Result was Err

expect_err(msg)

  • 仅适用于Result<T, E>
  • 作用类似于unwrap_err(),但panic时会带上自定义的错误信息。

例2.4:

let x: Result<i32, &str> = Err("error");
println!("{}", x.expect_err("Expected an error")); // 输出 "error"

let y: Result<i32, &str> = Ok(10);
println!("{}", y.expect_err("Expected an error")); // panic: Expected an error

总结

方法 适用类型 作用 遇到 NoneErr 时的行为
unwrap() Option<T> / Result<T, E> 获取 SomeOk 内部值 panic
unwrap_or(d) Option<T> / Result<T, E> 获取 SomeOk,否则返回 d 返回 d
unwrap_err() Result<T, E> 获取 Err 内部的错误值 panic
expect(msg) Option<T> / Result<T, E> 获取 SomeOk,panic 时显示 msg panic(带 msg
expect_err(msg) Result<T, E> 获取 Err 内部的错误值,panic 时显示 msg panic(带 msg

3 传播错误

当编写一个其实先会调用一些可能会失败的操作的函数时,除了在这个函数中处理错误外,还可以选择让调用者知道这个错误并决定该如何处理。这被称为传播(propagating)错误,这样能更好的控制代码调用。

例3.1:

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let username_file_result = File::open("hello.txt");

let mut username_file = match username_file_result {
Ok(file) => file,
Err(e) => return Err(e),
};

let mut username = String::new();

match username_file.read_to_string(&mut username) {
Ok(_) => Ok(username),
Err(e) => Err(e),
}
}

传播错误的简写:? 运算符

例3.2:

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}

如果Result的值是Ok,这个表达式将会返回Ok中的值而程序将继续执行。如果值是ErrErr将作为整个函数的返回值,就好像使用了return关键字一样,这样错误值就被传播给了调用者。

使用链式方法进一步缩短代码:

例3.3:

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let mut username = String::new();

File::open("hello.txt")?.read_to_string(&mut username)?;

Ok(username)
}