anyhowメモ

anyhowのメリット

  • エラーに説明を追加できる
  • anyhow::Result<T>は、エラーの伝播ルートをスタックトレースとして出力してくれる

anyhowがない場合

Result<T, E>std::error::Errorトレイトを使う。

fn count_words<R: Read>(input: &mut R) -> Result<u32, Box<dyn Error>> {
    let reader = BufReader::new(input);
    let mut wordcount = 0;
    for line in reader.lines() { // line = Result<String>
        for _word in line?.split_whitespace() {
            wordcount += 1;
        }
    }
    Ok(wordcount)
}

fn main() -> Result<(), Box<dyn Error>> {
    // 複数ファイル対応
    for filename in env::args().skip(1).collect::<Vec<String>>() {
        // どのファイルが開けなかったのか出力されない
        let mut reader = File::open(&filename)?;

        let wordcount = count_words(&mut reader)?;
        println!("{} {}", wordcount, filename);
    }
    Ok(())
}

anyhowがある場合

anyhow::Result<T>anyhow::Error型を使う。

use anyhow::{Result, Context};

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

fn count_words<R: Read>(input: R) -> Result<u32> {
    let reader = BufReader::new(input);
    let mut wordcount = 0;
    for line in reader.lines() {
        for _word in line.context("not line")?.split_whitespace() {
            wordcount += 1;
        }
    }

    Ok(wordcount)
}

fn run() -> Result<()> {
    for filename in env::args().skip(1).collect::<Vec<String>>() {
        // contextで、anyhow::Errorに変換し、説明を付与できる。
        let file = File::open(&filename).context(format!("unable to open {}", filename))?;
        let wordcount = count_words(&file).context(format!("unable to count words in {}", filename))?;
        println!("{} {}", wordcount, filename);
    }
    Ok(())
}

fn main() /*NG main関数ではResult<(), anyhow::Error>を返せない-> Result<()>*/ {
    if let Err(error) = run() {
        // Errorを出力する
        eprintln!("Error: {:?}", error);
        std::process::exit(0);
    }
}