m06-error-handling by zhanghandong/rust-skills
npx skills add https://github.com/zhanghandong/rust-skills --skill m06-error-handling第一层:语言机制
这个失败是预期的还是程序错误?
在选择错误处理策略之前:
| 模式 | 不要只说 | 而是应该问 |
|---|---|---|
| unwrap 导致恐慌 | "用 ?" | 这里 None/Err 真的可能出现吗? |
| ? 的类型不匹配 | "用 anyhow" | 错误类型设计正确吗? |
| 丢失错误上下文 | "加 .context()" | 调用者需要知道什么? |
| 错误变体太多 | "用 Box" | 错误的粒度合适吗? |
在处理错误之前:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
谁来处理这个错误?
需要什么上下文信息?
当错误策略不明确时:
"我应该返回 Result 还是 Option?"
↑ 问:缺失/失败是正常的还是异常的?
↑ 检查:m09-domain(领域模型怎么说?)
↑ 检查:domain-*(错误处理要求)
| 情况 | 追溯到 | 问题 |
|---|---|---|
| 太多 unwrap | m09-domain | 数据模型正确吗? |
| 错误上下文设计 | m13-domain-error | 需要什么恢复操作? |
| 库 vs 应用错误 | m11-ecosystem | 使用者是谁? |
从设计到实现:
"预期失败,库代码"
↓ 使用:thiserror 用于类型化错误
"预期失败,应用代码"
↓ 使用:anyhow 用于符合人体工学的错误处理
"缺失是正常的(查找、获取、查询)"
↓ 使用:Option<T>
"程序错误或不变量违反"
↓ 使用:panic!, assert!, unreachable!
"需要带上下文传播"
↓ 使用:.context("当时在做什么")
| 模式 | 何时使用 | 示例 |
|---|---|---|
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 |
失败是预期的吗?
├─ 是 → 缺失是唯一的"失败"吗?
│ ├─ 是 → Option<T>
│ └─ 否 → Result<T, E>
│ ├─ 库 → thiserror
│ └─ 应用 → anyhow
└─ 否 → 是程序错误吗?
├─ 是 → panic!, assert!
└─ 否 → 考虑是否真的不可恢复
用 ? → 需要上下文吗?
├─ 是 → .context("message")
└─ 否 → 直接用 ?
| 错误 | 原因 | 修复 |
|---|---|---|
unwrap() 恐慌 | 未处理的 None/Err | 使用 ? 或 match |
| 类型不匹配 | 不同的错误类型 | 使用 anyhow 或 From |
| 丢失上下文 | ? 没有上下文 | 添加 .context() |
cannot use ? | 缺少 Result 返回类型 | 返回 Result<(), E> |
| 反模式 | 为什么不好 | 更好的做法 |
|---|---|---|
到处用 .unwrap() | 生产环境恐慌 | .expect("reason") 或 ? |
| 静默忽略错误 | 隐藏错误 | 处理或传播 |
预期错误用 panic! | 用户体验差,无法恢复 | Result |
| 到处用 Box | 丢失类型信息 | thiserror |
| 何时 | 查看 |
|---|---|
| 领域错误策略 | m13-domain-error |
| 包边界 | m11-ecosystem |
| 类型安全错误 | m05-type-driven |
| 心智模型 | m14-mental-model |
每周安装量
652
代码仓库
GitHub 星标数
929
首次出现
2026年1月20日
安全审计
安装于
opencode596
codex578
gemini-cli567
github-copilot554
amp501
kimi-cli497
Layer 1: Language Mechanics
Is this failure expected or a bug?
Before choosing error handling strategy:
| Pattern | Don't Just Say | Ask Instead |
|---|---|---|
| unwrap panics | "Use ?" | Is None/Err actually possible here? |
| Type mismatch on ? | "Use anyhow" | Are error types designed correctly? |
| Lost error context | "Add .context()" | What does the caller need to know? |
| Too many error variants | "Use Box" | Is error granularity right? |
Before handling an error:
What kind of failure is this?
Who handles this?
What context is needed?
When error strategy is unclear:
"Should I return Result or Option?"
↑ Ask: Is absence/failure normal or exceptional?
↑ Check: m09-domain (what does domain say?)
↑ Check: domain-* (error handling requirements)
| Situation | Trace To | Question |
|---|---|---|
| Too many unwraps | m09-domain | Is the data model right? |
| Error context design | m13-domain-error | What recovery is needed? |
| Library vs app errors | m11-ecosystem | Who are the consumers? |
From design to implementation:
"Expected failure, library code"
↓ Use: thiserror for typed errors
"Expected failure, application code"
↓ Use: anyhow for ergonomic errors
"Absence is normal (find, get, lookup)"
↓ Use: Option<T>
"Bug or invariant violation"
↓ Use: panic!, assert!, unreachable!
"Need to propagate with context"
↓ Use: .context("what was happening")
| Pattern | When | Example |
|---|---|---|
Result<T, E> | Recoverable error | fn read() -> Result<String, io::Error> |
Option<T> | Absence is normal | fn find() -> Option<&Item> |
? | Propagate error | let data = file.read()?; |
unwrap() |
| Context | Error Crate | Why |
|---|---|---|
| Library | thiserror | Typed errors for consumers |
| Application | anyhow | Ergonomic error handling |
| Mixed | Both | thiserror at boundaries, anyhow internally |
Is failure expected?
├─ Yes → Is absence the only "failure"?
│ ├─ Yes → Option<T>
│ └─ No → Result<T, E>
│ ├─ Library → thiserror
│ └─ Application → anyhow
└─ No → Is it a bug?
├─ Yes → panic!, assert!
└─ No → Consider if really unrecoverable
Use ? → Need context?
├─ Yes → .context("message")
└─ No → Plain ?
| Error | Cause | Fix |
|---|---|---|
unwrap() panic | Unhandled None/Err | Use ? or match |
| Type mismatch | Different error types | Use anyhow or From |
| Lost context | ? without context | Add .context() |
cannot use ? |
| Anti-Pattern | Why Bad | Better |
|---|---|---|
.unwrap() everywhere | Panics in production | .expect("reason") or ? |
| Ignore errors silently | Bugs hidden | Handle or propagate |
panic! for expected errors | Bad UX, no recovery | Result |
| Box everywhere | Lost type info | thiserror |
| When | See |
|---|---|
| Domain error strategy | m13-domain-error |
| Crate boundaries | m11-ecosystem |
| Type-safe errors | m05-type-driven |
| Mental models | m14-mental-model |
Weekly Installs
652
Repository
GitHub Stars
929
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode596
codex578
gemini-cli567
github-copilot554
amp501
kimi-cli497
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
103,800 周安装
Convex 应用安全检查清单 | 身份验证、函数暴露、参数验证、行级访问控制、环境变量安全
1,500 周安装
Convex HTTP Actions 教程:构建Webhook、API集成与自定义路由端点
1,500 周安装
微信小程序开发指南 - 腾讯云CloudBase集成、调试发布与项目结构最佳实践
1,600 周安装
arXiv语义搜索工具 - 使用自然语言查询物理、数学、计算机科学预印本论文
586 周安装
软件架构开发指南:整洁架构与DDD最佳实践,提升代码质量与可维护性
1,600 周安装
数据可视化专家指南:图表选择、设计原则与代码示例
1,500 周安装
| Dev/test only |
config.get("key").unwrap() |
expect() | Invariant holds | env.get("HOME").expect("HOME set") |
panic! | Unrecoverable | panic!("critical failure") |
| Missing Result return |
Return Result<(), E> |