在 Rust 的世界中,錯誤處理是構建健壯且可靠應用程式的基石。與其他語言不同,Rust 將錯誤視為程式執行過程中不可或缺的一部份,並提供強大的工具來處理它們。其中,
thiserror
和
anyhow
這兩個庫脫穎而出,為開發者提供了優雅且高效的錯誤處理機制。
thiserror:為庫作者量身客製的錯誤型別定義利器
thiserror
的設計初衷是簡化自訂錯誤型別的定義,它提供了一個方便的衍生宏,用於實作標準庫
std::error::Error
trait。
thiserror 的核心功能
便捷的錯誤型別定義 : 使用
#[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 },
}
靈活的錯誤資訊格式化 :
#[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),
}
自動實作
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 的主要功能
統一的錯誤處理 : 使用
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)
}
豐富的錯誤上下文 : 使用
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))?;
// ...
}
靈活的錯誤向下轉換 : 支持按值、共享參照或可變參照進行向下轉換。
// 如果錯誤是由 redaction 引起的,則返回一個
// tombstone 而不是內容。
match root_cause.downcast_ref::<DataStoreError>() {
Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
None => Err(error),
}
自動回溯 : 在 Rust 1.65 及更高版本中,如果底層錯誤型別沒有提供自己的回溯,
anyhow
將捕獲並打印回溯。與自訂錯誤型別整合 :
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),
}
便捷的錯誤構建 : 提供
anyhow!
宏用於構建一次性錯誤訊息,支持字串插值;還提供bail!
宏作為提前返回錯誤的簡寫。
returnErr(anyhow!("Missing attribute: {}", missing));
bail!("Missing attribute: {}", missing);
no_std 支持 :
anyhow
支持no_std
模式,幾乎所有 API 都可用並以相同的方式工作。
棄用的錯誤處理庫
一些曾經廣泛使用的庫,例如
failure
和
error-chain
,由於長期缺乏維護,現在基本上已經被廢棄。
總結
thiserror
和
anyhow
是 Rust 生態系中兩個優秀的錯誤處理庫,它們為開發者提供了強大的工具來構建健壯且可靠的應用程式。
thiserror
專註於為庫作者提供便捷的自訂錯誤型別定義方式,而
anyhow
則致力於簡化應用程式級別的錯誤處理。選擇合適的工具取決於具體的套用場景。
文章精選
點 擊 關 註 並 掃 碼 添 加 進 交 流 群
領
取
「Rust
語
言
」
學
習
資
料