Rust: Error Handling

Error Handling

Unrecoverable Errors with panic!

Rust 的 可靠性:错误处理,大部分情况下:在编译时提示错误并处理

错误分类:

可恢复:例如文件未找到,可再次尝试

不可恢复:例如访问索引超出范围

默认情况下,当 panic 发生:

  • 程序展开调用栈 (工作量大)
  • Rust 沿着调用栈回走
  • 清理每个遇到的函数中的数据
  • 或 立即终止调用栈
  • 不清理内存,直接停止程序,内存需要 OS 进行清理

panic!

产生 panic 打印路径,行等信息

panic!("crash");

// thread 'main' panicked at src\main.rs:3:5:crash

panic!可能出现在我们写的代码中,我们所依赖的代码中

通过设置环境变量 RUST_BACKTRACE 可以得到回溯信息

Recoverable Errors with Result

Result 枚举

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

操作成功返回 Ok(T) 中的数据

操作失败返回 Err(E) 中的数据

例如打开一个文件, 返回 Result 类型

use std::fs::File;

fn main() {
    let frs = File::open("some.txt");
    let x = match frs {
        Ok(file) => file,
        Err(error) => {
            panic!("Not find file {:#?}", error);
        }
    };
}

输出信息

thread 'main' panicked at src\main.rs:9:13:
Not find file Os {
    code: 2,
    kind: NotFound,
    message: "系统找不到指定的文件。",
}

传播错误,将错误返回,让调用者决定如何处理

例如

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
    let frs = File::open(path);
    let mut f = match frs {
        Ok(file) => file,
        Err(err) => return Err(err),
    };

    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

? 运算符 传播错误的一种快捷方式

例如,与上面功能一样

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

// let mut f = File::open(path)?;
// 				等于
// let mut f = match frs {
// 		Ok(file) => file,
//     	Err(err) => return Err(err),
// };

? 作用于 Result ,成功即 Ok(T) 中的 T, 失败即 return Err

链式调用的方式

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
    let mut s = String::new();
    File::open(path)?.read_to_string(&mut s)?;
    Ok(s)
}

To panic! or Not to panic!

示例、 代码原型和测试都非常适合 panic

当我们比编译器知道更多的情况,当你有一些其他的逻辑来确保 Result 会是 Ok 值时,调用 unwrap 或者 expect 也是合适的

错误处理指导原则:在当有可能会导致有害状态的情况下建议使用 panic!

当错误预期会出现时,返回 Result 仍要比调用 panic! 更为合适

对于错误处理的话,比较优雅的一种做法是定义一个全局的错误枚举,比如dragon OS的system error枚举。

这样的话,错误可以很方便的在不同模块之间进行传递