关于 m15-anti-pattern
聚焦设计层反模式的 Rust 代码审查技能,将过度克隆、生产代码中使用 unwrap 等问题对应到替代方案和相关技能。
主要功能
- 01将反模式对应到替代方案
- 02检查所有权与错误处理
- 03将代码异味关联到相关技能
- 04提供 Rust 审查清单
适用场景
- 01审查 Rust 代码中的过度克隆
- 02查找生产代码里的 unwrap
- 03从借用检查器阻碍追溯设计问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill m15-anti-pattern安装后在 Claude Code 中输入/m15-anti-pattern确认已加载。
附带文件patterns/common-mistakes.md
详细介绍
反模式
第 2 层:设计选择
核心问题
这种模式是否掩盖了设计问题?
审查代码时:
- 这是在解决症状还是根因?
- 有没有更符合惯用方式的做法?
- 这与 Rust 的设计理念相悖,还是顺势而为?
反模式 → 更好的模式
| 反模式 | 问题所在 | 更好的做法 |
|---|---|---|
到处使用 .clone() | 掩盖所有权问题 | 使用恰当的引用或所有权 |
在生产环境中使用 .unwrap() | 可能在运行时触发 panic | 使用 ?、expect 或错误处理 |
只有一个所有者时仍使用 Rc | 造成不必要的开销 | 使用简单所有权 |
为了方便而使用 unsafe | 存在 UB 风险 | 寻找安全的实现方式 |
通过 Deref 实现 OOP | API 容易造成误解 | 使用组合和 trait |
| 巨大的 match 分支 | 难以维护 | 提取为方法 |
到处使用 String | 浪费内存分配 | 使用 &str、Cow<str> |
忽略 #[must_use] | 丢失错误 | 处理结果或使用 let _ = |
思考提示
看到可疑代码时:
-
这是症状还是根因?
- 为了避免借用问题而克隆? → 所有权设计有问题
- 因为“不会失败”而使用 unwrap? → 未处理边界情况
-
符合惯用方式的代码会是什么样?
- 使用引用,而不是克隆
- 使用迭代器,而不是索引循环
- 使用模式匹配,而不是标志位
-
这是否与 Rust 的设计理念相悖?
- 与借用检查器较劲 → 重新组织代码
- 过度使用 unsafe → 寻找安全的实现方式
向上追溯 ↑
了解设计思路:
"Why does my code have so many clones?"
↑ Ask: Is the ownership model correct?
↑ Check: m09-domain (data flow design)
↑ Check: m01-ownership (reference patterns)
| 反模式 | 追溯到 | 问题 |
|---|---|---|
| 到处克隆 | m01-ownership | 谁应该拥有这些数据? |
| 到处使用 unwrap | m06-error-handling | 错误处理策略是什么? |
| 到处使用 Rc | m09-domain | 所有权是否明确? |
| 与生命周期较劲 | m09-domain | 是否应该更改数据结构? |
向下追溯 ↓
查看实现(第 1 层):
"Replace clone with proper ownership"
↓ m01-ownership: Reference patterns
↓ m02-resource: Smart pointer if needed
"Replace unwrap with proper handling"
↓ m06-error-handling: ? operator
↓ m06-error-handling: expect with message
初学者最常犯的 5 个错误
| 排名 | 错误 | 修正方法 |
|---|---|---|
| 1 | 通过克隆绕过借用检查器 | 使用引用 |
| 2 | 在生产环境中使用 unwrap | 使用 ? 传播错误 |
| 3 | 所有场景都使用 String | 使用 &str |
| 4 | 使用索引循环 | 使用迭代器 |
| 5 | 与生命周期较劲 | 重新组织代码,让数据由当前结构拥有 |
代码异味 → 重构
| 异味 | 表明 | 重构方式 |
|---|---|---|
大量 .clone() | 所有权不明确 | 理清数据流 |
大量 .unwrap() | 缺少错误处理 | 添加适当的错误处理 |
大量 pub 字段 | 封装被破坏 | 改为私有字段 + 访问器 |
| 嵌套过深 | 逻辑复杂 | 提取方法 |
| 函数过长 | 承担多项职责 | 拆分函数 |
| 巨大的枚举 | 缺少抽象 | 使用 trait + 类型 |
常见错误模式
| 错误 | 反模式成因 | 修正方法 |
|---|---|---|
| E0382 移动后使用 | 混淆克隆与所有权 | 使用恰当的引用 |
| 在生产环境中 panic | 到处使用 unwrap | 使用 ?、模式匹配 |
| 性能低下 | 所有文本都使用 String | 使用 &str、Cow |
| 与借用检查器较劲 | 结构不合理 | 重新组织代码 |
| 内存膨胀 | 到处使用 Rc/Arc | 使用简单所有权 |
已弃用 → 更好的做法
| 已弃用 | 更好的做法 |
|---|---|
| 基于索引的循环 | 使用 .iter()、.enumerate() |
先使用 collect::<Vec<_>>() 再迭代 | 链接迭代器 |
| 手动实现 unsafe cell | 使用 Cell、RefCell |
使用 mem::transmute 进行类型转换 | 使用 as 或 TryFrom |
| 自定义链表 | 使用 Vec、VecDeque |
lazy_static! | 使用 std::sync::OnceLock |
快速审查清单
- 每次使用
.clone()都有合理依据 - 库代码中不使用
.unwrap() - 有不变量的字段不公开为
pub - 迭代器可用时不使用索引循环
-
&str足够时不使用String - 不忽略
#[must_use]警告 - 使用
unsafe时附带 SAFETY 注释 - 不编写巨型函数(超过 50 行)
相关技能
| 场景 | 参阅 |
|---|---|
| 所有权模式 | m01-ownership |
| 错误处理 | m06-error-handling |
| 心智模型 | m14-mental-model |
| 性能 | m10-performance |
评论
支持 Markdown