Rustで良さげなエラーメッセージを出力 w/ anyhow, thiserror
CLIのツールを作るときに、Rustのanyhow
, thiserror
を使ってこう書いとけばとりあえず困らなそう、というところまで来たのでメモとして記します。
Cargo.toml
Cargo.toml
のdependenciesにanyhow
とthiserror
を追加します。
[dependencies]
anyhow = "1.0"
thiserror = "1.0"
main.rs
main.rs
では、冒頭にanyhow
, thiserror
のcontext
やerror
を使えるように次のように書いておきます。
use anyhow::{self, Context};
use thiserror::Error;
自前のエラーはthiserror::Error
を用いて、エラーメッセージのフォーマットとともに定義します。「My...」や「my ...」等は実際のアプリケーションの名前や実際のエラーメッセージ等に置き換えます。
#[derive(Error, Debug)]
pub enum MyAppError {
#[error("line {}: my decode error", .linenum)] // 付加的な情報を持つエラーメッセージの場合
MyDecodeError { linenum: usize },
#[error("my fuga error")] // 付加的な情報を持たないエラーメッセージの場合
MyFugaError,
}
main関数はanyhow::Result<()>
を戻り値にするものに決め打ちします。
fn main() -> anyhow::Result<()> {
....
Ok(())
}
(ここでanyhow::Result
としているのは、単にResut
とした場合には、あとでuseを足したときにstd::result::Result
やstd::io::Result
などを指してしまうことがあるので、あらかじめ明示しておいてそれを避けるためです。)
条件判定してエラーにする場合
自前で定義したエラーを作り.into()
でanyhow::Error
に変換するような書き方にします。
if 条件 {
return Err(MyAppError::MyDecodeError { linenum: li + 1 }.into())?;
}
if 条件 {
return Err(MyAppError::MyFugaError.into())?;
}
ファイル読み込みなどのエラーから、自前のエラーに変換する場合
.unrwap()
を使うとpanicしたようなエラーメッセージが出力されてしまうので、context
, with_context
を使って書いていきます。
let fp = File::open(input_file).with_context(|| format!("fail to open file: {}", input_file))?;
for (li, line) in io::BufReader::new(fp).lines().enumerate() {
let line = line.context(MyAppError::MyDecodeError { linenum: li + 1 })?;
....
}
さらに短く書きたい!
(1) anyhow!
マクロを使うとその場でエラーを生成できます。1箇所でしか起きないエラーなら、anyhow!
マクロを使って生成するとお手軽です。
return Err(anyhow!("given value is too large {}", value));
その場合は、main.rs
の冒頭でマクロを使うことを明示します。
#[macro_use]
extern crate anyhow;
use anyhow::Context;
use thiserror::Error;
(2) thiserrorでエラーを定義するときに、値に名前を付けなくてよければタプルを使うことができます。
#[derive(Error, Debug)]
pub enum MyAppError {
#[error("line {}: my decode error", .0)] // 付加的な情報を持つエラーメッセージの場合
MyDecodeError(usize),
}
let line = line.context(MyAppError::MyDecodeError(li + 1))?;
追記
- anyhowに関してより詳しい記事を見つけました (2022.04.02)
Rust/AnyhowのTips https://zenn.dev/yukinarit/articles/b39cd42820f29e
Discussion