anyhow库提供了一个 anyhow::Error 类型,基于 std::error::Error trait上面做的扩展,可以很方便的用于进行 Rust 程序的错误处理。

快速使用

如果需要在项目中添加 anyhow 依赖,可以使用如下命令:

cargo add anyhow

也可以直接编辑 Cargo.toml 文件,并添加一下内容:

[dependencies]
anyhow = "1.0"

注意:需要使用 rustc 1.39+版本

用法

  • 使用Result<T, anyhow::Error>或者等效的anyhow::Result<T>,来替换任何返回Result<T, E>类型的函数返回值。
use anyhow::Result;

fn get_cluster_info() -> Result<ClusterMap> {
    let config = std::fs::read_to_string("cluster.json")?;
    let map: ClusterMap = serde_json::from_str(&config)?;
    Ok(map)
}
  • 附加context上下文可以有效的帮助开发人员排查问题到底出在哪里。如果没有更多的上下文信息,返回像“No such file or directory”这样的低级错误信息就很烦人,因为看不出来具体到底是哪个文件或者目录不存在。
use anyhow::{Context, Result};

fn main() -> Result<()> {
    ...
    it.detach().context("Failed to detach the important thing")?;

    let content = std::fs::read(path)
        .with_context(|| format!("Failed to read instrs from {}", path))?;
    ...
}
Error: Failed to read instrs from ./path/to/instrs.json

Caused by:
    No such file or directory (os error 2)
  • 支持使用downcast检查具体的错误类型,也可以做类型替换。
// If the error was caused by redaction, then return a
// tombstone instead of the content.
match root_cause.downcast_ref::<DataStoreError>() {
    Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
    None => Err(error),
}
  • 如果使用Rust版本 ≥ 1.65,如果底层错误类型没有提供自己的错误类型,则会捕获并打印一个错误backtrace。为了查看backtrace信息,必须通过std::backtrace中描述的环境变量来启用backtrace:

    • 如果你想让panic和error都有backtrace,设置RUST_BACKTRACE=1
    • 如果你只想让 error 有 backtrace,设置RUST_LIB_BACKTRACE=1
    • 如果你只想让 panic 有 backtrace,设置RUST_BACKTRACE=1RUST_LIB_BACKTRACE=0
  • anyhow适用于任何实现了std::error::Error trait的错误类型,包括你在自己的 crate 中定义的错误类型。我们不绑定derive(Error)宏,但是你可以使用thiserror库来快速定义错误。

use thiserror::Error;

#[derive(Error, Debug)]
pub enum FormatError {
    #[error("Invalid header (expected {expected:?}, got {found:?})")]
    InvalidHeader {
        expected: String,
        found: String,
    },
    #[error("Missing attribute: {0}")]
    MissingAttribute(String),
}
  • 可以使用anyhow!宏来构建一次性的错误信息,该宏支持通过字符串和一些变量插值来定义anyhow::Error
return Err(anyhow!("Missing attribute: {}", missing));

等效的也可以使用bail!宏:

bail!("Missing attribute: {}", missing);

No-std支持

在 no_std 模式下,几乎所有相同的 API 都可用并且以相同的方式工作。 如果要在 no_std 模式下添加依赖 anyhow,请在 Cargo.toml 中禁用我们默认启用的std功能。 需要一个全局allocator。

[dependencies]
anyhow = { version = "1.0", default-features = false }

由于基于?的错误转换依赖于 std 下面的std::error::Error trait,所以如果在 no-std 模式下,处理非 anyhow 错误类型时,需要显式的使用.map_err(Error::msg)来返回一个 anyhow 的错误类型。