Rust 错误分层:把排障信息留在正确位置

背景 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 错误处理做得好,排障效率会明显提升。 ...

2026年4月24日 · 1 分钟 · BvBeJ

Rust 错误处理:从 panic 到 anyhow

C++ 错误处理的痛 C++ 里错误处理方式一大堆,但没一个完美的: // 方式1: 返回值 + 特殊值 int get_value() { if (failed) return -1; // -1 是魔法值 } // 方式2: 异常 try { do_something(); } catch (const std::exception& e) { // 异常才是正文... } 异常的问题是:不知道会抛什么,不知道该不该 catch,析构函数里抛异常还会 std::terminate。 Rust 的错误哲学 Rust 把错误分为两类: 可恢复错误 → Result<T, E> 不可恢复错误 → panic! // 可恢复:用 Result fn read_file(path: &str) -> Result<String, std::io::Error> { std::fs::read_to_string(path) } // 不可恢复:用 panic fn main() { let v = vec![1, 2, 3]; v.get(10).expect("索引超出范围"); // 程序员的bug } 实战:错误处理的几种模式 1. 基本用法 use std::fs::File; use std::io::{self, Read}; fn read_config() -> Result<String, io::Error> { let mut file = File::open("config.toml")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents) } fn main() { match read_config() { Ok(config) => println!("配置: {}", config), Err(e) => eprintln!("读取配置失败: {}", e), } } ? 操作符是灵魂——错误自动向上传播,不需要手写 match。 ...

2026年4月9日 · 2 分钟 · BvBeJ