关于 m03-mutability
面向 Rust 编程的技能,帮助判断数据是否需要可变以及如何管理可变性。内容说明借用规则,并按线程环境和数据类型比较内部可变性方案。
主要功能
- 01判断是否需要修改数据
- 02比较内部可变性模式
- 03运用借用规则
- 04选择线程安全的修改方式
适用场景
- 01通过重构解决借用冲突
- 02为单线程代码选择 Cell 或 RefCell
- 03为并发数据选择 Mutex、RwLock 或原子类型
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill m03-mutability安装后在 Claude Code 中输入/m03-mutability确认已加载。
详细介绍
可变性
第 1 层:语言机制
核心问题
为什么这份数据需要改变?谁可以改变它?
在添加内部可变性之前,请先了解:
- 可变性是必需的,还是意外复杂性?
- 谁应该控制可变性?
- 这种可变模式安全吗?
错误 → 设计问题
| 错误 | 不要只说 | 应该这样问 |
|---|---|---|
| E0596 | “添加 mut” | 它真的应该是可变的吗? |
| E0499 | “拆分借用” | 数据结构是否合适? |
| E0502 | “分隔作用域” | 为什么我们需要同时持有这两种借用? |
| RefCell panic | “使用 try_borrow” | 运行时检查是否合适? |
思考提示
添加可变性之前:
-
可变性是否必要?
- 也许可以转换 → 返回新值
- 也许可以使用构建器 → 以不可变方式构造
-
谁控制可变性?
- 外部调用方 →
&mut T - 内部逻辑 → 内部可变性
- 并发访问 → 同步可变性
- 外部调用方 →
-
线程环境是什么?
- 单线程 → Cell/RefCell
- 多线程 → Mutex/RwLock/Atomic
向上追溯 ↑
当可变性冲突持续出现时:
E0499/E0502 (borrow conflicts)
↑ Ask: Is the data structure designed correctly?
↑ Check: m09-domain (should data be split?)
↑ Check: m07-concurrency (is async involved?)
| 持续出现的错误 | 追溯到 | 问题 |
|---|---|---|
| 反复发生借用冲突 | m09-domain | 是否应该重构数据? |
| async 中使用 RefCell | m07-concurrency | 是否需要 Send/Sync? |
| Mutex 死锁 | m07-concurrency | 锁的设计是否合适? |
向下追溯 ↓
从设计到实现:
"Need mutable access from &self"
↓ T: Copy → Cell<T>
↓ T: !Copy → RefCell<T>
"Need thread-safe mutation"
↓ Simple counters → AtomicXxx
↓ Complex data → Mutex<T> or RwLock<T>
"Need shared mutable state"
↓ Single-thread: Rc<RefCell<T>>
↓ Multi-thread: Arc<Mutex<T>>
借用规则
At any time, you can have EITHER:
├─ Multiple &T (immutable borrows)
└─ OR one &mut T (mutable borrow)
Never both simultaneously.
快速参考
| 模式 | 线程安全 | 运行时开销 | 适用场景 |
|---|---|---|---|
&mut T | 不适用 | 零 | 独占可变访问 |
Cell<T> | 否 | 零 | Copy 类型,不需要引用 |
RefCell<T> | 否 | 运行时检查 | 非 Copy 类型,需要运行时借用 |
Mutex<T> | 是 | 锁竞争 | 线程安全的可变性 |
RwLock<T> | 是 | 锁竞争 | 多读者、少写者 |
Atomic* | 是 | 极小 | 简单类型(bool、usize) |
错误代码参考
| 错误 | 原因 | 快速修复 |
|---|---|---|
| E0596 | 将不可变借用为可变 | 添加 mut 或重新设计 |
| E0499 | 多个可变借用 | 重构代码流程 |
| E0502 | 存在 & 时使用 &mut | 分隔借用作用域 |
内部可变性决策
| 场景 | 选择 |
|---|---|
| T: Copy,单线程 | Cell<T> |
| T: !Copy,单线程 | RefCell<T> |
| T: Copy,多线程 | AtomicXxx |
| T: !Copy,多线程 | Mutex<T> 或 RwLock<T> |
| 多线程、读操作频繁 | RwLock<T> |
| 简单标志/计数器 | AtomicBool、AtomicUsize |
反模式
| 反模式 | 不足之处 | 更好的做法 |
|---|---|---|
| 到处使用 RefCell | 运行时 panic | 明确所有权设计 |
| 单线程场景使用 Mutex | 不必要的开销 | RefCell |
| 忽略 RefCell panic | 难以调试 | 处理或重构 |
| 在热点循环中加锁 | 严重影响性能 | 批量操作 |
相关技能
| 场景 | 参见 |
|---|---|
| 智能指针选择 | m02-resource |
| 线程安全 | m07-concurrency |
| 数据结构设计 | m09-domain |
| 反模式 | m15-anti-pattern |
评论
支持 Markdown