Skip to content

33.5 新的 Failure 库

标准库中现存的 Error trait 有几个明显问题:

  • description 方法基本没什么用;

  • 无法回溯,它没有记录一层层的错误传播的过程,不方便 debug;

  • Box<Error>不是线程安全的。

Failure 这个库就是为了进一步优化错误处理而设计的。它主要包含三个部分。

  • 新的failure::Fail trait,是为了取代std::error::Error trait 而设计的。它包含了更丰富的成员方法,且继承于 Send+Sync,具备线程安全特性。

  • 自动 derive 机制,主要是让编译器帮用户写一些重复性的代码。

  • failure::Error结构体。所有其他实现了 Fail trait 的错误类型,都可以转换成这个类型,而且它提供了向下转型的方法。

使用 failure 来实现前面那个示例,代码如下:


rust
#[macro_use]
extern crate failure;

use std::fs::File;
use std::io::Read;
use std::path::Path;

#[derive(Debug, Fail)]
enum MyError {
    #[fail(display = "IO error {}.", _0)]
    Io(#[cause] std::io::Error),
    #[fail(display = "Parse error {}.", _0)]
    Parse(#[cause] std::num::ParseIntError),
}

impl From<std::io::Error> for MyError {
    fn from(error: std::io::Error) -> Self {
        MyError::Io(error)
    }
}

impl From<std::num::ParseIntError> for MyError {
    fn from(error: std::num::ParseIntError) -> Self {
        MyError::Parse(error)
    }
}

fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, MyError> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let n = contents.trim().parse::<i32>()?;
    Ok(2 * n)
}


fn main() {
    match file_double("foobar") {
        Ok(n) => println!("{}", n),
        Err(err) => println!("Error: {:?}", err),
    }
}

现在社区里已经有一些库转向使用 failure 做错误处理。它将来可能是 Rust 生态系统中主流的错误处理方式。

Released under the MIT License