跳到主要内容

错误处理策略设计

问题

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

相关链接