错误处理策略设计
问题
Rust 项目中应该如何设计错误处理策略?
答案
错误处理分层
分层策略
| 层级 | 工具 | 原则 |
|---|---|---|
| 库(被别人调用) | thiserror | 定义精确的错误枚举 |
| 应用(最终消费) | anyhow | 统一错误,关注上下文 |
| 边界(API 接口) | 手动转换 | 错误 → HTTP 状态码 + 消息 |
库层:thiserror
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DatabaseError {
#[error("connection failed: {0}")]
ConnectionFailed(String),
#[error("query failed: {0}")]
QueryFailed(#[from] sqlx::Error),
#[error("record not found: {table}.{id}")]
NotFound { table: String, id: i64 },
}
应用层:anyhow
use anyhow::{Context, Result};
async fn process_order(order_id: i64) -> Result<()> {
let order = db.get_order(order_id)
.await
.context("failed to fetch order")?; // 添加上下文
let user = db.get_user(order.user_id)
.await
.with_context(|| format!("failed to fetch user {}", order.user_id))?;
send_email(&user.email, &order)
.await
.context("failed to send confirmation email")?;
Ok(())
}
API 边界:错误转 HTTP 响应
use axum::{response::IntoResponse, http::StatusCode, Json};
enum AppError {
NotFound(String),
Internal(anyhow::Error),
Validation(String),
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, message) = match self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
AppError::Internal(err) => {
tracing::error!("Internal error: {:?}", err);
(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error".into())
}
AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg),
};
(status, Json(serde_json::json!({ "error": message }))).into_response()
}
}
常见面试问题
Q1: 什么时候用 panic,什么时候用 Result?
答案:
| 场景 | 用什么 |
|---|---|
| 不可恢复的编程错误 | panic!(如数组越界、不变量被破坏) |
| 可预期的失败 | Result(如文件不存在、网络超时) |
| 原型/脚本 | unwrap()(快速迭代) |
| 生产代码 | ? + context()(总是处理错误) |
经验法则:如果调用者可能想要处理这个错误 → Result;如果是 bug → panic。