六、错误处理

16 阅读3分钟

错误处理是指如何在编程语言中处理程序中遇到的错误。在 rust 中,将错误类型分为可恢复错误和不可恢复错误。可恢复错误:通常表示业务预期内的错误,可以重试解决,例如调用下游超时。不可恢复错误:程序也不知道要怎么处理,例如数组访问越界。

在 Rust 中,使用 panic! 宏命令来处理不可恢复错误。它可以打印一个错误信息,展开并清理数据,然后退出程序。

fn main() {
    panic!("crash and burn")
}

// cargo run
// thread 'main' panicked at hello/src/main.rs:8:5:
// crash and burn
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

// RUST_BACKTRACE=1  cargo run, 打印 backtrace 信息
// thread 'main' panicked at hello/src/main.rs:8:5:
// crash and burn
// stack backtrace:
// 0: rust_begin_unwind
// at /rustc/89e2160c4ca5808657ed55392620ed1dbbce78d1/library/std/src/panicking.rs:645:5
// 1: core::panicking::panic_fmt
// at /rustc/89e2160c4ca5808657ed55392620ed1dbbce78d1/library/core/src/panicking.rs:72:14
// 2: hello::main
 // at ./src/main.rs:8:5
// 3: core::ops::function::FnOnce::call_once
// at /rustc/89e2160c4ca5808657ed55392620ed1dbbce78d1/library/core/src/ops/function.rs:250:5
// note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. 

程序中的大多数错误还么有严重到需要程序完全停止执行。在 Rust 中,使用 Result 枚举作为返回值来处理潜在的错误。同时,Reuslt 定义了一系列方法方便对错误进行处理。

// T, E 是范型类型参数,Reustl 是一个枚举类型,OK(T):包含的是正常返回的数据,Err(E): 包含的是错误信息
pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

fn main() {
    // unwrap() 方法,如果 Result 包含错误直接 panic
let num = "127".parse::<i64>().unwrap();
    println!("{}", num);
    // expect() 方法,如果 Result 包含错误直接 panic, 并携带定义的 msg
let num = "12-7".parse::<i64>().expect("invalid str number");
    println!("{}", num)
}

以打开文件为例,当文件打开失败时,如果文件不存在则创建文件,如果是其他的错误类型则直接退出程序。在这个例子中使用了大量 match 来完了错误处理逻辑。为了方便开发,Result 也接受闭包方法方便错误处理。

fn main() {
    let f = match File::open("hello.txt") {
        // 模式匹配返回的 Result<std::fs::File, std::fs::Error>
 Ok(f) => f,
        Err(e) => match e.kind() {
            // 拿到 Error 之后, 匹配 error 的类型
ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("create file meet err {:?}", e),
            },
            // 匹配其他类型
other_err => panic!("open file meet err {:?}", other_err),
        },
    };
}

// 使用闭包函数
fn main() {
    let f = File::open("hello.txt").unwrap_or_else(|error| {
        // 当遇到 error 时执行闭包函数
    if error.kind() == ErrorKind::NotFound {
            File::create("hello.tex") // 当 create 遇到 error, 执行闭包函数
    .unwrap_or_else(|error| panic!("create file meet err {:?}", error))
        } else {
            panic!("open file meet err {:?}", error)
        }
    });
}

对于错误的错误可以使用不可恢复错误,也可以使用可恢复错误。在大多数情况下,建议使用可恢复错误,将具体的错误信息返回给业务方,让其进行额外的处理。而对于一些示例、代码原型和测试场景,并不需要代码那么健壮,直观的进行 panic 反而是最方便的。