anyhow = “1.0.97”
https://docs.rs/anyhow/latest/anyhow/
anyhowを使うと、エラー処理が簡単にできるようになる。
anyhow::Errorはエラー型のラッパー、あらゆるエラー型からanyhow::Errorに変換できる。変換できる。
Result
contextやwith_contextを使うと、Noneをanyhow::Errorとして移譲することも可能
anyhow::Result
use anyhow::{ensure, Context, Result}; use std::fs; #[tokio::main] async fn main() { let path_123 = "./data/temp/123.txt"; println!("{}", with_unwrap(&path_123)); // let path_abc = "./data/temp/abc.txt"; // println!("{}", with_unwrap(&path_abc)); let path_non_existent = "./data/to/non_existent.txt"; println!("{}", with_unwrap(&path_non_existent)); } fn with_unwrap(path: &str) -> i32 { let s = fs::read_to_string(path).unwrap(); let i = s.parse::<i32>().unwrap(); i * 10 }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.76s
Running `target/debug/app`
1230
thread ‘main’ panicked at src/main.rs:16:30:
called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: “No such file or directory” }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
vagrant@vagrant:~/dev/rust/app$
エラーメッセージが、InvalidDigit、No such file or directoryでそれぞれ異なる。
#[tokio::main] async fn main() { let path_123 = "./data/temp/123.txt"; println!("{}", with_unwrap(&path_123).unwrap()); let path_abc = "./data/temp/abc.txt"; println!("{:?}", with_unwrap(&path_abc).unwrap_err()); let path_non_existent = "./data/to/non_existent.txt"; println!("{:?}", with_unwrap(&path_non_existent).unwrap_err()); } fn with_unwrap(path: &str) -> Result<i32> { let s = fs::read_to_string(path)?; let i = s.parse::<i32>()?; Ok(i * 10) }
1230
invalid digit found in string
No such file or directory (os error 2)
### anyhow::Contextでコンテキストを追加
with_contextは遅延評価でエラーの時のみ指定
fn with_unwrap(path: &str) -> Result<i32> { let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?; let i = s.parse::<i32>() .with_context(|| format!("Failed to parse {s:?}"))?; Ok(i * 10) }
1230
Failed to parse “abc”
Caused by:
invalid digit found in string
Failed to read “./data/to/non_existent.txt”
Caused by:
No such file or directory (os error 2)
### OptionをResultに変換
contextおよび with_contextはResultだけでなく、Optionからも呼べる。Noneも呼び出し元に移譲できる。
async fn main() { let path_max = "data/temp/max.txt"; fs::write(path_max, i32::MAX.to_string()).unwrap(); println!("{:?}", with_anyhow_option(path_max).unwrap_err()); let path_123 = "./data/temp/123.txt"; println!("{}", with_unwrap(&path_123).unwrap()); let path_abc = "./data/temp/abc.txt"; println!("{:?}", with_unwrap(&path_abc).is_err()); let path_non_existent = "./data/to/non_existent.txt"; println!("{:?}", with_unwrap(&path_non_existent).is_err()); } fn with_anyhow_option(path: &str) -> Result<i32> { let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?; let i = s.parse::<i32>() .with_context(|| format!("Failed to parse {s:?}"))?; let i = i.checked_add(100).context("Overflow occurred")?; Ok(i) }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.86s
Running `target/debug/app`
Overflow occurred
1230
true
true
### マクロ(anyhow!, bail!, ensure!)で単発のエラーを返す
ensureで値に応じてエラーとすることもできる。
fn with_anyhow_macro(path: &str) -> Result<i32> { let s = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?; let i = s.parse::<i32>() .with_context(|| format!("Failed to parse {s:?}"))?; let i = i.checked_add(100).context("Overflow occurred")?; ensure!(i >= 1000, "Value must be at least 1000, got {}", i); Ok(i) }
match resultでの処理
let path_123 = "./data/temp/123.txt"; let result = with_anyhow_macro(&path_123); match result { Ok(n) => { println!("{}", n); } Err(error) => { println!("{}", error); } }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.81s
Running `target/debug/app`
Value must be at least 1000, got 223