示例说明
字符串错误丢失结构,调用方只能匹配文本。thiserror 派生的错误枚举让每个失败模式成为一等类型:NotFound 带着缺失的 SKU、InsufficientStock 带着现有与请求量——match 可以精确分支处理而不用解析消息。#[from] 属性自动生成 From 转换,问号传播外部错误时零样板。
技术要点
- 变体载荷即结构化的错误数据
- from 属性自动生成转换
- Display 就近写在变体声明处
代码解读
InventoryError 五个变体覆盖典型失败模式:两种业务错误带字段载荷、IO 与序列化经 from 转换保留源错误、过期错误带秒数。reserve 按条件返回对应变体;main 的两个 match 演示载荷解构匹配与错误链打印。fetch_manifest 里两个问号分别触发两种 From 转换。
使用建议
库边界用 thiserror 给调用方稳定 API,应用顶层用 anyhow 汇聚。变体数量增长时拆分子枚举组合。#[from] 源错误保存在链上可逐层追溯。
代码示例
use thiserror::Error;
#[derive(Debug, Error)]
pub enum InventoryError {
#[error("sku {sku} not found in catalog")]
NotFound { sku: String },
#[error("insufficient stock: have {available}, requested {requested}")]
InsufficientStock { available: u32, requested: u32 },
#[error("warehouse service unreachable")]
Unreachable(#[from] std::io::Error),
#[error("serialization of manifest failed")]
Serialization(#[from] serde_json::Error),
#[error("reservation expired after {seconds}s")]
Expired { seconds: u64 },
}
pub type InventoryResult<T> = Result<T, InventoryError>;
fn reserve(sku: &str, requested: u32) -> InventoryResult<u32> {
let stock: u32 = if sku == "SKU-9" { 2 } else { 120 };
if sku == "SKU-0" {
return Err(InventoryError::NotFound { sku: sku.into() });
}
if requested > stock {
return Err(InventoryError::InsufficientStock {
available: stock,
requested,
});
}
Ok(stock - requested)
}
fn fetch_manifest(path: &str) -> InventoryResult<String> {
let raw = std::fs::read_to_string(path)?; // io::Error 自动 From 转换
let value: serde_json::Value = serde_json::from_str(&raw)?;
Ok(value.to_string())
}
fn main() {
match reserve("SKU-9", 5) {
Ok(left) => println!("reserved, {left} left"),
Err(err) => println!("reserve failed: {err}"),
}
match reserve("SKU-0", 1) {
Ok(_) => unreachable!(),
Err(InventoryError::NotFound { sku }) => println!("missing sku: {sku}"),
Err(other) => println!("other: {other}"),
}
println!("{:?}", fetch_manifest("missing.json").map(|s| s.len()));
}