Skip to content

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_thenOption 链的核心
  • 严禁 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())

说明:

  • 用于缓存、容错
  • 禁止无日志吞错

四、OptionResult 转换(关键)

1. OptionResult

rust
opt.ok_or(MyError::NotFound)?
opt.ok_or_else(|| MyError::NotFound)?

适用:

  • 数据缺失即错误

2. ResultOption

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
  • 库 / 服务?→ 精确错误类型

Lljxww's Fantasy, Powered by VitePress Visits: