m03-可变性Skill m03-mutability

这是一个关于Rust编程语言中可变性管理的技能指南。它详细解释了如何正确处理可变数据、借用规则、内部可变性模式以及线程安全。核心内容包括:Rust可变性设计原则、借用检查器错误解析、Cell/RefCell/Mutex/RwLock等内部可变性工具的选择指南、常见反模式以及向上/向下追溯问题根源的方法。关键词:Rust可变性,借用检查,内部可变性,Cell,RefCell,Mutex,RwLock,线程安全,所有权,借用规则,Rust错误处理。

后端开发 0 次安装 0 次浏览 更新于 2/27/2026

name: m03-mutability description: “关键:用于可变性问题。触发条件:E0596, E0499, E0502, cannot borrow as mutable, already borrowed as immutable, mut, &mut, interior mutability, Cell, RefCell, Mutex, RwLock, 可变性, 内部可变性, 借用冲突” user-invocable: false

可变性

第一层:语言机制

核心问题

为什么这些数据需要改变,以及谁可以改变它?

在引入内部可变性之前,请理解:

  • 可变性是必需的还是偶然的复杂性?
  • 谁应该控制可变性?
  • 可变模式是否安全?

错误 → 设计问题

错误代码 不要只是说 而是应该问
E0596 “添加 mut” 这真的需要可变吗?
E0499 “拆分借用” 数据结构设计正确吗?
E0502 “分离作用域” 为什么我们需要两种借用?
RefCell 恐慌 “使用 try_borrow” 运行时检查是否合适?

思考提示

在添加可变性之前:

  1. 可变是必要的吗?

    • 也许可以转换 → 返回新值
    • 也许可以使用构建器 → 以不可变方式构造
  2. 谁控制可变性?

    • 外部调用者 → &mut T
    • 内部逻辑 → 内部可变性
    • 并发访问 → 同步可变性
  3. 线程上下文是什么?

    • 单线程 → Cell/RefCell
    • 多线程 → Mutex/RwLock/Atomic

向上追溯 ↑

当可变性冲突持续存在时:

E0499/E0502 (借用冲突)
    ↑ 提问:数据结构设计正确吗?
    ↑ 检查:m09-domain (数据应该拆分吗?)
    ↑ 检查:m07-concurrency (是否涉及异步?)
持续错误 追溯至 问题
重复的借用冲突 m09-domain 数据应该重组吗?
async 中的 RefCell m07-concurrency 需要 Send/Sync 吗?
Mutex 死锁 m07-concurrency 锁设计正确吗?

向下推导 ↓

从设计到实现:

"需要从 &self 进行可变访问"
    ↓ T: Copy → Cell<T>
    ↓ T: !Copy → RefCell<T>

"需要线程安全的可变"
    ↓ 简单计数器 → AtomicXxx
    ↓ 复杂数据 → Mutex<T> 或 RwLock<T>

"需要共享可变状态"
    ↓ 单线程:Rc<RefCell<T>>
    ↓ 多线程:Arc<Mutex<T>>

借用规则

在任何时刻,你只能拥有以下之一:
├─ 多个 &T (不可变借用)
└─ 或者一个 &mut T (可变借用)

永远不能同时拥有两者。

快速参考

模式 线程安全 运行时成本 使用场景
&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 运行时恐慌 清晰的所有权设计
单线程使用 Mutex 不必要的开销 RefCell
忽略 RefCell 恐慌 难以调试 处理或重构
在热循环中加锁 性能杀手 批量操作

相关技能

何时 参见
智能指针选择 m02-resource
线程安全 m07-concurrency
数据结构设计 m09-domain
反模式 m15-anti-pattern