Rust 错误处理清单
Rust 没有异常机制(Exception),所有错误都必须被显式建模、传播或处理。 这既是约束,也是 Rust 在大型工程中可靠性的根本来源。
一、错误模型总览(先统一认知)
| 类型 | 语义 | 适用场景 |
|---|---|---|
panic! | 程序缺陷 | 不可恢复的 bug |
Option<T> | 有 / 无 | 合法缺失 |
Result<T, E> | 成功 / 失败 | 业务或系统错误 |
核心原则:
panic只用于 不该发生的情况- 业务错误必须用
Result - 合法缺失用
Option
二、Option<T> 处理方案
1. 强制解包(极少用)
rust
let v = opt.unwrap();
let v = opt.expect("should exist");适用:
- 测试
- 确定性不变量
不适用:
- 任何线上逻辑
2. 提供默认值(最常见)
rust
opt.unwrap_or(0)
opt.unwrap_or_else(|| compute())
opt.unwrap_or_default()说明:
unwrap_or_else惰性执行,优先选unwrap_or_default要求T: Default
3. 条件继续(链式)
rust
opt.map(|v| v.len())
opt.and_then(|v| lookup(v))
opt.or_else(|| fallback())说明:
and_then是 Option 链的核心- 严禁
if let Some层层嵌套
三、Result<T, E> 处理方案
1. 基本模式
rust
fn work() -> Result<T, E> {
Ok(v)
}2. 错误传播(首选)
rust
let v = do_work()?;要求:
- 函数返回
Result - 错误类型兼容(或可
From)
3. 错误映射
rust
result.map(|v| v.len())
result.map_err(|e| MyError::Io(e))适用:
- 底层错误 → 领域错误
4. 降级 / 兜底
rust
result.or_else(|_| fallback())说明:
- 用于缓存、容错
- 禁止无日志吞错
四、Option ↔ Result 转换(关键)
1. Option → Result
rust
opt.ok_or(MyError::NotFound)?
opt.ok_or_else(|| MyError::NotFound)?适用:
- 数据缺失即错误
2. Result → Option
rust
result.ok()⚠️ 风险:
- 错误信息丢失
- 只能用于明确允许失败的路径
五、自定义错误类型(生产必备)
1. 手写 enum
rust
enum AppError {
NotFound,
Io(std::io::Error),
}缺点:
- 模板代码多
2. thiserror(强烈推荐)
rust
#[derive(thiserror::Error, Debug)]
pub enum AppError {
#[error("not found")]
NotFound,
#[error("io error")]
Io(#[from] std::io::Error),
}优势:
- 自动实现
From - 错误链完整
- 适合 library / service 层
六、统一错误类型策略
1. anyhow(应用层首选)
rust
use anyhow::{Result, Context};
fn run() -> Result<()> {
let cfg = load()
.context("load config failed")?;
Ok(())
}特点:
- 少类型设计成本
- 强上下文能力
- 不适合库
2. Box<dyn Error>
rust
Result<T, Box<dyn std::error::Error>>缺点:
- 类型信息弱
- 不利于模式匹配
七、错误上下文(必须做)
rust
fs::read_to_string(path)
.with_context(|| format!("read file {}", path))?;原则:
- 边界处加 context
- 不要在每一层都加
八、Web / API 层最佳实践(axum 示例)
rust
pub type ApiResult<T> = Result<T, AppError>;rust
impl IntoResponse for AppError {
fn into_response(self) -> Response {
match self {
AppError::NotFound => StatusCode::NOT_FOUND.into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}
}原则:
- Handler 不
unwrap - 错误统一映射 HTTP
九、日志与错误的边界
rust
match do_work() {
Ok(v) => v,
Err(e) => {
log::error!("{:?}", e);
return Err(e);
}
}规则:
- 只在边界打日志
- 中间层只返回错误
十、分层错误处理规范(强制)
| 层 | 错误策略 |
|---|---|
| domain | 精确 enum |
| service | 错误组合 |
| infra | 原始错误 |
| api | 错误转换 |
| main | 日志 + 退出 |
十一、反模式清单(禁止)
❌ unwrap() 在线上代码
❌ 用 panic! 表达业务失败
❌ Result<T, String>
❌ .ok() 吞错不留痕
❌ handler 里写 match 巨石
十二、工程级总结
错误不是“处理掉”,而是“表达清楚并正确传递”。
判断口诀:
- 这是 bug?→
panic - 允许不存在?→
Option - 需要上层知道?→
Result - 应用入口?→
anyhow - 库 / 服务?→ 精确错误类型