Day 2:anyhow——簡化錯誤傳播
學習目標
- 用 anyhow 讓錯誤處理更簡單
- 用 Context 加附帶訊息
- 理解 anyhow vs thiserror 的分工
今日重點
安裝
[dependencies]
anyhow = "1"
用 anyhow::Result 取代 Box
use anyhow::Result;
fn read_config() -> Result<String> {
let contents = std::fs::read_to_string("config.toml")?;
Ok(contents)
}
anyhow::Result<T> 是 Result<T, anyhow::Error> 的別名。anyhow::Error 可以容納**任何**錯誤,並自動幫你加上錯誤來源鏈。
Context 加上下文
use anyhow::{Result, Context};
struct Config { /* ... */ }
fn load_config() -> Result<Config> {
let contents = std::fs::read_to_string("config.toml")
.context("Failed to read config file")?;
let config = basic_load(&contents)
.context("Failed to parse config")?;
Ok(config)
}
.context() 在錯誤上附加「發生在哪一步」的說明,比直接的底層錯誤好懂很多。
anyhow vs thiserror 分工
| 工具 |
場景 |
角色 |
| thiserror |
library 定義具體錯誤型別 |
給呼叫者精確的、可 match 的錯誤 |
| anyhow |
application 快速傳播/加上下文 |
不想細確型別,只想要好的錯誤消息 |
| > 常見組合:library 用 thiserror 定義錯誤,application 的 main 用 anyhow 把這些錯誤往上傳。 |
|
|
練習
use anyhow::{Result, Context};
fn read_number(path: &str) -> Result<i32> {
let text = std::fs::read_to_string(path)
.with_context(|| format!("failed to read {}", path))?;
let n: i32 = text.trim().parse()
.with_context(|| format!("failed to parse '{}' as number", text.trim()))?;
Ok(n)
}
1. 建一個不存在檔案測試錯誤訊息可讀性。
2. 嘗試
println!("{:?}", err) 看錯誤鏈。
自我檢查
深入連結
- anyhow crate 文件(docs.rs/anyhow)