本章要回答的三个问题:可恢复错误怎么在类型系统里表达?什么时候该 panic、什么时候该返回 Result?自定义错误类型怎么组织与转换?
多数语言把错误分成"异常"与"返回值"两套机制,Rust 只保留返回值这一套主流路径,并把"成功还是失败"直接建进函数签名——Result 枚举。调用方不处理就编不过,Java 的受检异常想做的事,这里用类型完成了。panic 则保留给"程序自身缺陷"类不可恢复情况,相当于当庭宣判退庭。
? 链式传播错误,不再层层 if;| 节 | 回答哪个问题 | 关键产出 |
|---|---|---|
| 6.1 Result 与问号 | 可恢复错误怎么传播 | ? 语义、链式调用样式 |
| 6.2 Panic | 哪些情况退庭了结 | panic 触发条件、与 Result 的分界 |
| 6.3 自定义错误 | 库的错误怎么设计 | 错误枚举、From 自动转换 |
第 7 章工程组织中,测试用例将专门断言 Result 与 panic 的行为;自定义错误类型是库发布前的最后一道工序。
| 报错/现象 | 案情 | 修法方向 |
|---|---|---|
E0277 + ? |
函数不返回 Result 却用了 ? |
改返回值或 match |
| From 缺失 | ? 两端错误类型不通 |
手写 From 或 map_err |
| main 崩溃码 101 | main 返回了 Err | 属预期,配好错误信息即可 |
| 跨 FFI 崩溃 | panic 穿越 extern 边界 | catch_unwind 拦截 |
| 维度 | 可恢复(Result) | 不可恢复(panic) |
|---|---|---|
| 判据 | 调用方可能补救 | 程序不变量被破坏 |
| 席位 | 外部输入、IO、网络 | 数组下标、断言、契约 |
| 传播 | ? 与错误链 |
unwind 到线程边界 |
| 测试态度 | 用 is_err 断言 | 用 should_panic 断言 |
分界线的口语版:坏数据用 Result,坏代码用 panic。拿不准时倾向 Result——把 panic 降级为错误永远比反向改造容易。
错误处理与测试是一对互审关系:Result 的每个 Err 分支都该有测试触达,panic 路径靠 should_panic 站岗。第 7 章的测试举证一节会直接引用本章的 CaseError 判例作为被测对象,建议保留代码到下一章复用。
团队协作的底线可以压缩成四条:外部输入路径禁止 unwrap/expect;库的公开错误实现 Error 与 Display;应用顶层统一出口打印 source 链;测试覆盖每个 Err 分支。四条都不难,难的是长期不松。守住的回报在半年后:线上事故的根因定位从小时级降到分钟级,因为每层错误都带着自己的判词和证物链。
一问:? 用在返回 () 的函数里会怎样?答:E0277,? 需要可匹配的 Err 出口。二问:Box 里那个 'static 约束拦住了什么?答:携带非静态引用的错误类型,即"错误必须自持数据"。三问:catch_unwind 能拦住 panic=abort 的程序吗?答:不能,abort 直接终止进程。三题答案都在正文判例里,能脱口而出的部分就是已经内化的部分,其余回庭重读对应小节即可。
生态里两个高频名字一次说清:thiserror 面向库作者,用 derive 宏生成第 6.3 节手写的四件套(Display、Error、From),零运行时成本;anyhow 面向应用作者,提供带上下文包装的动态错误类型,等价于增强版 Box。选型口诀是"库 thiserror、应用 anyhow,别在库里用 anyhow"——库的错误类型是公共契约,动态错误会把契约稀释成字符串。两库的用法与手写版一一对应,理解了 6.3 的骨架,两个库各十分钟就能上手,也随时能在不引依赖时退回手写。