thiserror and anyhow

The thiserror and anyhow crates are widely used to simplify error handling.

  • thiserror 經常在程式庫中使用,目的是建立可實作 From<T> 的自訂錯誤型別。
  • anyhow 經常由應用程式使用,目的是協助函式中的錯誤處理機制,包括為錯誤加上背景資訊。
use anyhow::{bail, Context, Result};
use std::fs;
use std::io::Read;
use thiserror::Error;

#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[error("Found no username in {0}")]
struct EmptyUsernameError(String);

fn read_username(path: &str) -> Result<String> {
    let mut username = String::with_capacity(100);
    fs::File::open(path)
        .with_context(|| format!("Failed to open {path}"))?
        .read_to_string(&mut username)
        .context("Failed to read")?;
    if username.is_empty() {
        bail!(EmptyUsernameError(path.to_string()));
    }
    Ok(username)
}

fn main() {
    //fs::write("config.dat", "").unwrap();
    match read_username("config.dat") {
        Ok(username) => println!("Username: {username}"),
        Err(err) => println!("Error: {err:?}"),
    }
}
This slide should take about 5 minutes.

thiserror

  • Error 衍生巨集是由 thiserror 提供,附有許多實用的屬性,有助於以精簡方式定義錯誤型別。
  • std::error::Error 特徵會自動衍生。
  • #[error] 的訊息則用於衍生 Display 特徵。

anyhow

  • anyhow::Error 基本上是 Box<dyn Error> 周遭的包裝函式。因此,通常也是不建議程式庫的公用 API 使用,但可在應用程式中廣泛使用。
  • anyhow::Result<V>Result<V, anyhow::Error> 的型別別名。
  • 必要時,可以擷取其中的實際錯誤類型進行檢查。
  • Go 開發人員可能會覺得 anyhow::Result<T> 提供的功能似曾相識,因為該功能提供了與 Go 中的 (T, error) 類似的使用模式和人體工學。
  • anyhow::Context 是針對標準 ResultOption 型別實作的特徵。如要啟用這些型別的 .context().with_context(),就必須使用 anyhow::Context。