name: m01-所有权与生命周期 description: “关键:用于所有权/借用/生命周期问题。触发:E0382、E0597、E0506、E0507、E0515、E0716、E0106,值已移动、借用值生命周期不足、无法移出、使用已移动的值、所有权、借用、生命周期、'a、'static、移动、克隆、Copy、所有权、借用、生命周期” user-invocable: false
所有权与生命周期
第一层:语言机制
核心问题
谁应该拥有这些数据,以及拥有多久?
在修复所有权错误之前,请理解数据的角色:
- 它是共享的还是独占的?
- 它是短命的还是长命的?
- 它是被转换还是仅被读取?
错误 → 设计问题
| 错误代码 | 不要只说 | 而是问 |
|---|---|---|
| E0382 | “克隆它” | 谁应该拥有这些数据? |
| E0597 | “延长生命周期” | 作用域边界是否正确? |
| E0506 | “先结束借用” | 修改操作是否应该在其他地方发生? |
| E0507 | “移动前先克隆” | 为什么我们要从引用中移动数据? |
| E0515 | “返回拥有所有权的值” | 调用者应该拥有数据吗? |
| E0716 | “绑定到变量” | 为什么这是临时的? |
| E0106 | “添加 'a” | 实际的生命周期关系是什么? |
思考提示
在修复所有权错误之前,请先问:
-
这些数据在领域中的角色是什么?
- 实体(唯一标识)→ 拥有所有权
- 值对象(可互换)→ 克隆/复制 OK
- 临时(计算结果)→ 可能需要重构
-
所有权设计是有意的吗?
- 按设计 → 在约束内工作
- 意外 → 考虑重新设计
-
修复症状还是重新设计?
- 如果第3次尝试 → 升级到第2层
向上追溯 ↑
当错误持续存在时,追溯到设计层:
E0382 (值已移动)
↑ 问:是什么设计选择导致了这种所有权模式?
↑ 检查:m09-领域模型(这是实体还是值对象?)
↑ 检查:domain-*(适用哪些约束?)
| 持续错误 | 追溯到 | 问题 |
|---|---|---|
| E0382 重复 | m02-资源管理 | 应该使用 Arc/Rc 进行共享吗? |
| E0597 重复 | m09-领域模型 | 作用域边界位置正确吗? |
| E0506/E0507 | m03-可变性 | 应该使用内部可变性吗? |
向下推导 ↓
从设计决策到实现:
"数据需要被不可变地共享"
↓ 使用:Arc<T>(多线程)或 Rc<T>(单线程)
"数据需要独占所有权"
↓ 使用:移动语义,获取所有权
"数据是只读视图"
↓ 使用:&T(不可变借用)
快速参考
| 模式 | 所有权 | 成本 | 使用时机 |
|---|---|---|---|
| 移动 | 转移 | 零 | 调用者不需要数据 |
&T |
借用 | 零 | 需要只读访问 |
&mut T |
独占借用 | 零 | 需要修改 |
clone() |
复制 | 分配 + 复制 | 确实需要一个副本 |
Rc<T> |
共享(单线程) | 引用计数 | 单线程内共享 |
Arc<T> |
共享(多线程) | 原子引用计数 | 多线程间共享 |
Cow<T> |
写时克隆 | 如果发生修改则分配 | 可能修改 |
错误代码参考
| 错误代码 | 原因 | 快速修复 |
|---|---|---|
| E0382 | 值已移动 | 克隆、使用引用或重新设计所有权 |
| E0597 | 引用比所有者寿命长 | 延长所有者作用域或重构 |
| E0506 | 借用期间赋值 | 在修改前结束借用 |
| E0507 | 从借用中移出 | 克隆或使用引用 |
| E0515 | 返回局部引用 | 返回拥有所有权的值 |
| E0716 | 临时值被丢弃 | 绑定到变量 |
| E0106 | 缺少生命周期标注 | 添加 'a 标注 |
反模式
| 反模式 | 为什么不好 | 更好的做法 |
|---|---|---|
到处使用 .clone() |
掩盖设计问题 | 正确设计所有权 |
| 与借用检查器对抗 | 增加复杂性 | 与编译器协作 |
对所有东西都用 'static |
限制灵活性 | 使用适当的生命周期 |
使用 Box::leak 导致泄漏 |
内存泄漏 | 正确的生命周期设计 |
相关技能
| 当需要时 | 参见 |
|---|---|
| 需要智能指针 | m02-资源管理 |
| 需要内部可变性 | m03-可变性 |
| 数据是领域实体 | m09-领域模型 |
| 学习所有权概念 | m14-心智模型 |