當前位置: 妍妍網 > 碼農

Rust 錯誤處理:thiserror vs. anyhow

2024-08-29碼農

在 Rust 的世界中,錯誤處理是構建健壯且可靠應用程式的基石。與其他語言不同,Rust 將錯誤視為程式執行過程中不可或缺的一部份,並提供強大的工具來處理它們。其中, thiserror anyhow 這兩個庫脫穎而出,為開發者提供了優雅且高效的錯誤處理機制。

thiserror:為庫作者量身客製的錯誤型別定義利器

thiserror 的設計初衷是簡化自訂錯誤型別的定義,它提供了一個方便的衍生宏,用於實作標準庫 std::error::Error trait。

thiserror 的核心功能

  1. 便捷的錯誤型別定義 : 使用 #[derive(Error, Debug)] 註解,可以輕松地為列舉或結構體實作 Error trait,從而將其轉換為自訂錯誤型別。

use thiserror::Error;
#[derive(Error, Debug)]
pubenumMyError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(#[from] std::num::ParseIntError),
#[error("Custom error: {msg}")]
Custom { msg: String },
}

  1. 靈活的錯誤資訊格式化 : #[error("...")] 內容支持占位符,可以方便地參照錯誤型別中的欄位,生成結構化的錯誤資訊。

#[derive(Error, Debug)]
pubenumFormatExample {
#[error("Error code {code}")]
Simple { code: i32 },
#[error("Complex error: {msg} (code: {code})")]
Complex { msg: String, code: i32 },
#[error("Error with source: {0}")]
WithSource(#[source] AnotherError),
}

  1. 自動實作 From trait : #[from] 內容可以自動實作 From trait,方便地將其他錯誤型別轉換為自訂錯誤型別。

#[derive(Error, Debug)]
pubenumMyError {
// ...
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
// ...
}

thiserror 與 Anyhow 的比較

thiserror 側重於為庫作者提供定義明確的錯誤型別,而 anyhow 更適合在應用程式開發中處理各種可能的錯誤情況。

thiserror 的局限性

  • 僅適用於列舉和結構體。

  • 不支持泛型錯誤型別,但可以在列舉變體中使用泛型。

  • anyhow:應用程式級別錯誤處理的利器

    anyhow 提供了 anyhow::Error ,這是一個基於 trait 的錯誤型別,用於在 Rust 應用程式中輕松處理各種錯誤。

    anyhow 的主要功能

    1. 統一的錯誤處理 : 使用 Result<T, anyhow::Error> anyhow::Result<T> 作為任何可能失敗的函式的返回型別,並使用 ? 運算子輕松地傳播任何實作 std::error::Error trait 的錯誤。

    use anyhow::Result;
    fnget_cluster_info() -> Result<ClusterMap> {
    let config = std::fs::read_to_string("cluster.json")?;
    let map: ClusterMap = serde_json::from_str(&config)?;
    Ok(map)
    }

    1. 豐富的錯誤上下文 : 使用 context with_context 方法為低階錯誤添加更多上下文資訊,有助於故障排除。

    use anyhow::{Context, Result};
    fnmain() -> Result<()> {
    // ...
    it.detach().context("Failed to detach the important thing")?;
    let content = std::fs::read(path)
    .with_context(|| format!("Failed to read instrs from {}", path))?;
    // ...
    }

    1. 靈活的錯誤向下轉換 : 支持按值、共享參照或可變參照進行向下轉換。

    // 如果錯誤是由 redaction 引起的,則返回一個
    // tombstone 而不是內容。
    match root_cause.downcast_ref::<DataStoreError>() {
    Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
    None => Err(error),
    }

    1. 自動回溯 : 在 Rust 1.65 及更高版本中,如果底層錯誤型別沒有提供自己的回溯, anyhow 將捕獲並打印回溯。

    2. 與自訂錯誤型別整合 : anyhow 可以與任何實作 std::error::Error 的錯誤型別一起使用,包括在你的 crate 中定義的錯誤型別。

    use thiserror::Error;
    #[derive(Error, Debug)]
    pubenumFormatError {
    #[error("Invalid header (expected {expected:?}, got {found:?})")]
    InvalidHeader {
    expected: String,
    found: String,
    },
    #[error("Missing attribute: {0}")]
    MissingAttribute(String),
    }

    1. 便捷的錯誤構建 : 提供 anyhow! 宏用於構建一次性錯誤訊息,支持字串插值;還提供 bail! 宏作為提前返回錯誤的簡寫。

    returnErr(anyhow!("Missing attribute: {}", missing));
    bail!("Missing attribute: {}", missing);

    1. no_std 支持 : anyhow 支持 no_std 模式,幾乎所有 API 都可用並以相同的方式工作。

    棄用的錯誤處理庫

    一些曾經廣泛使用的庫,例如 failure error-chain ,由於長期缺乏維護,現在基本上已經被廢棄。

    總結

    thiserror anyhow 是 Rust 生態系中兩個優秀的錯誤處理庫,它們為開發者提供了強大的工具來構建健壯且可靠的應用程式。 thiserror 專註於為庫作者提供便捷的自訂錯誤型別定義方式,而 anyhow 則致力於簡化應用程式級別的錯誤處理。選擇合適的工具取決於具體的套用場景。

    文章精選

    「Rust