问题修复
Rust 错误处理:根据失败是预期还是 bug 选择…
在 Rust 中决定用 Result、Option、panic 还是 ? 传播错误,并设计错误类型与上下文时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
Rust 错误处理:根据失败是预期还是 通用
解决思路
- 先问自己:这个失败是正常运行时可能发生的(预期),还是程序 bug?据此选择策略。
- 按失败类型选择:预期失败用 Result<T, E>;缺失是正常情况用 Option<T>;bug/不变量违反用 panic!;不可恢复用 panic!。
- 确定谁来处理:调用方处理就用 ? 传播;当前函数处理就用 match/if-let;给用户看就返回友好错误信息;给程序员看就 panic 并带消息。
- 确定需要什么上下文:需要错误类型就用 thiserror 变体;需要调用链就用 anyhow::Context;需要调试信息就用 anyhow 或 tracing。
- 遇到 unwrap panic 时不要只说「用 ?」,先问 None/Err 在这里是否真的可能。
- 遇到 ? 类型不匹配时不要只说「用 anyhow」,先问错误类型是否设计正确。
- 遇到错误上下文丢失时不要只说「加 .context()」,先问调用方需要知道什么。
- 遇到错误变体过多时不要只说「用 Box<dyn Error>」,先问错误粒度是否合适。
- 错误策略不清晰时向上追溯:问缺失/失败是正常还是异常,检查 m09-domain 领域模型,检查 domain-* 的错误处理需求。
- unwrap 过多时追溯到 m09-domain,问数据模型是否正确。
- 错误上下文设计时追溯到 m13-domain-error,问需要什么恢复能力。
- 区分库与应用错误时追溯到 m11-ecosystem,问消费者是谁。
- 预期失败且是库代码:用 thiserror 定义类型化错误。
- 预期失败且是应用代码:用 anyhow 做易用的错误处理。
- 缺失是正常情况(find、get、lookup):用 Option<T>。
- bug 或不变量违反:用 panic!、assert!、unreachable!。
- 需要带上下文传播:用 .context("what was happening")。
- 可恢复错误用 Result<T, E>,例如 fn read() -> Result<String, io::Error>。
- 缺失正常用 Option<T>,例如 fn find() -> Option<&Item>。
- 传播错误用 ?,例如 let data = file.read()?;。
- unwrap() 仅用于开发/测试,例如 config.get("key").unwrap()。
- expect() 用于不变量成立时,例如 env.get("HOME").expect("HOME set")。
- 不可恢复时用 panic!,例如 panic!("critical failure")。
- 库用 thiserror(为消费者提供类型化错误),应用用 anyhow(易用),混合场景在边界用 thiserror、内部用 anyhow。
- 用 ? 后需要上下文就加 .context("message"),不需要就用普通 ?。
- 修复 unwrap() panic:改用 ? 或 match。
- 修复类型不匹配:用 anyhow 或 From。
- 修复上下文丢失:在 ? 处加 .context()。
- 修复 cannot use ?:让函数返回 Result<(), E>。
- 避免到处 .unwrap():改用 .expect("reason") 或 ?。
- 避免静默忽略错误:处理或传播。
- 避免对预期错误用 panic!:改用 Result。
- 避免到处用 Box<dyn Error>:改用 thiserror 保留类型信息。
适用边界 · 注意事项
- 失败是正常运行时预期会发生的情况时,不要用 panic! 或 unwrap()。
- 在库代码中不要用 anyhow 作为对外错误类型,会丢失类型信息。
- 不要静默忽略错误,会隐藏 bug。