anyhow

anyhow クレートは、追加のコンテキスト情報を保持できるリッチなエラー型を提供します。これは、エラーに至るまでプログラムが何をしていたかの意味的なトレースを提供するために使用できます。

これを thiserror の便利なマクロと組み合わせることで、カスタムエラー型に対するトレイト実装を明示的に書く必要を避けられます。

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

use anyhow::{Context, Result, bail};
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:?}"),
    }
}
  • anyhow::Error は本質的には Box<dyn Error> のラッパーです。そのため、これも一般にライブラリの公開 API にはあまり適した選択肢ではありませんが、アプリケーションでは広く使われています。
  • anyhow::Result<V>Result<V, anyhow::Error> の型エイリアスです。
  • anyhow::Error が提供する機能は Go 開発者にはなじみがあるかもしれません。これは Go の error 型に似た振る舞いを提供し、Result<T, anyhow::Error> は Go の (T, error) によく似ています(ただし、ペアの要素のうち意味を持つのは片方だけという慣習があります)。
  • anyhow::Context は標準の Result 型および Option 型に対して実装されているトレイトです。これらの型で .context().with_context() を有効にするには use anyhow::Context が必要です。

さらに調べるには

  • anyhow::Errorstd::any::Any と同様にダウンキャストをサポートしています。必要であれば、内部に格納されている具体的なエラー型を取り出して調べることができます。これには Error::downcast を使用します。