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::Result
  • 用 context 添加訊息
  • 懂 anyhow vs thiserror 的分工
  • 能讀出 anyhow 錯誤鏈

深入連結

  • anyhow crate 文件(docs.rs/anyhow)