第四章 错误处理与 Result/Option
核心能力标签:错误类型设计 · 传播机制 · 自定义错误 · panic 控制
知识结构:错误处理四个层次
| 层次 | 关键概念 | 面试高频考点 |
|---|---|---|
| 基础层 | Option / Result 枚举 | unwrap/expect 滥用风险 |
| 传播层 | ? 运算符、From trait | 错误类型转换链 |
| 自定义层 | thiserror / anyhow / Display + Error | 错误原因链 (source) |
| 控制层 | panic! / catch_unwind / 钩子 | panic 安全与终止策略 |
面试问答卡(3组)
🟢 Q1:Rust 中如何区分使用 Result 和 panic?
| 考察点 | 错误哲学理解、panic 边界判断、代码健壮性 |
| 回答框架 | ① 分类:可恢复错误用 Result,不可恢复用 panic;② 典型 panic 场景:数组越界、除零、逻辑断言;③ 业务错误一律用 Result;④ 库代码避免 panic,留错误处理给调用方。 |
| 示例回答 | “Rust 的核心原则是:可恢复错误用 Result 返回,让调用方决定;不可恢复错误用 panic!终止。例如文件不存在用 Result,下标越界则 panic。我编写库时,所有预期错误都返回 Result,仅在内部逻辑 bug 时 panic。” (约85字) |
| 可能追问 | 如何自定义 panic 行为?catch_unwind 是否推荐? |
| 避坑提醒 | ❌ 不要用 panic 代替普通错误处理;❌ 不要在库中随意 unwrap;✅ 生产代码中 panic 前应记录关键上下文。 |
🟢 Q2:? 运算符如何实现错误类型自动转换?
| 考察点 | From trait 理解、错误链设计、标准库机制 |
| 回答框架 | ① ? 调用 From::from 将错误类型转为函数返回的错误类型;② 需要为自定义错误实现 From<其他错误>;③ 常见模式:使用 thiserror 或 anyhow 简化;④ 错误链的 source 方法提供根因。 |
| 示例回答 | “? 会调用 Into::into 自动转换错误类型。例如函数返回 MyError,内部使用 std::io::Error,只需 impl From<io::Error> for MyError,? 就能自动转换。我用 thiserror 宏快速派生,避免手写样板代码。” (约90字) |
| 可能追问 | 多个错误类型如何统一?anyhow 和 thiserror 各自适用场景? |
| 避坑提醒 | ❌ 忘记为自定义错误实现 From 导致编译失败;✅ 二进制程序多用 anyhow,库多用 thiserror;✅ 错误类型应实现 std::error::Error。 |
🟢 Q3:如何设计一个生产可用的自定义错误类型?
| 考察点 | 枚举设计、Display/Source 实现、thiserror 使用 |
| 回答框架 | ① 使用枚举变体区分错误类别;② 每个变体可携带额外数据(如错误码、上下文);③ 派生 thiserror 快速实现 Display 和 Error;④ 提供简洁的 From 实现;⑤ 考虑序列化(如 serde)用于 API 返回。 |
| 示例回答 | “我会用 enum 定义:`#[derive(thiserror::Error)] pub enum MyError { #[error(\"IO error: {0}\")] Io(#[from] io::Error), #[error(\"Not found: {id}\")] NotFound { id: u64 } }`。每个变体包含针对性数据,自动实现 Error trait,便于上游匹配处理。” (约100字) |
| 可能追问 | 如何支持错误链?如何给错误添加 HTTP 状态码映射? |
| 避坑提醒 | ❌ 不要把所有错误包装成一个字符串类型;❌ 避免过度设计导致变体过多;✅ 为每个变体提供 clear 的文档注释。 |
项目案例:分布式配置中心错误处理设计
| 项目名称 | ZebraConfig — 高可用配置中心 agent |
| 业务背景 | 微服务集群需要实时拉取配置,agent 负责监听变更并更新本地文件,要求错误处理覆盖网络、文件、序列化、校验等场景 |
| 规模指标 | 1000+ 节点,每秒 2000+ 配置变更事件,QPS 峰值 5000+ |
| 技术栈 | Rust + tokio + thiserror + anyhow + serde + tracing |
| 核心难点 | 多种错误类型统一管理;支持重试与降级;panic 隔离不崩溃主循环;错误日志可追踪 |
| 面试高频追问 | 如何避免单个文件错误导致整个 agent 挂掉?如何处理配置文件损坏后的回滚?panic 在异步任务中如何捕获? |
| 可迁移回答话术 | “我们使用统一错误枚举,每个操作返回 Result<T, AgentError>,核心循环对每个任务 spawn 并 catch_unwind 捕获 panic,记录结构化日志后继续处理下一个任务。文件损坏时先备份再尝试旧版本恢复,失败则主动上报 metrics 并进入降级模式。” (约100字) |
💡 面试加分点:
• 提到
• 提及使用
• 主动说明通过
• 提到
#[track_caller] 定位 panic 源• 提及使用
eyre 或 color-eyre 增强错误报告• 主动说明通过
abort / unwind 配置控制 panic 策略
⚠️ 高频踩坑区:
• unwrap / expect 在异步上下文可能造成 task 泄露
• 过度使用 catch_unwind 会掩盖逻辑错误
• 不要忽略
• unwrap / expect 在异步上下文可能造成 task 泄露
• 过度使用 catch_unwind 会掩盖逻辑错误
• 不要忽略
? 运算符的隐式转换带来的性能开销(极小,但可讨论)