背景
Rust 的 Result 很强,但很多项目还是会遇到同一个问题:
- 错误被一路
?传上去 - 日志里只有一个模糊报错
- 出问题时不知道是哪个环节失败
这通常是错误分层没有做好。
一条实用原则
- 库层定义结构化错误类型
- 应用层补充上下文并统一输出
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RepoError {
#[error("record not found")]
NotFound,
#[error("db error: {0}")]
Database(String),
}
use anyhow::{Context, Result};
pub async fn get_user_handler(id: i64, svc: &UserService) -> Result<UserDto> {
let user = svc
.find_user(id)
.await
.with_context(|| format!("get user failed, id={id}"))?;
Ok(UserDto::from(user))
}
日志里要带可关联字段
只打印错误文本通常不够,至少带上:
- 请求 ID
- 用户或租户标识
- 关键资源 ID
tracing::error!(
request_id = %request_id,
user_id = user_id,
error = %err,
"failed to get user"
);
总结
Rust 错误处理做得好,排障效率会明显提升。
核心不是“有没有 ?”,而是:
- 错误类型是否表达业务语义
- 上下文是否在边界处补全
- 日志是否可检索、可关联
好的错误信息,是线上稳定性的一部分。