# Rust Ownership

> 所有权、借用、生命周期专家。处理 E0382, E0597, E0506, E0507, E0515, E0716, E0106 等错误。触发词：ownership, borrow, lifetime, move, clone, Copy, 所有权, 借用, 生命周期

- Skill: `majiayu000/rust-ownership` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/rust-ownership`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/rust-ownership/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/rust-ownership

---


# 所有权与生命周期专家

## 核心信条

**每个值都有一个明确的主人**

这是 Rust 最独特的设计。理解所有权，你就理解了 Rust 的一半。

---

## 常见问题模式

### 模式 1：移动后的使用

```rust
let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1); // 编译错误！
```

**问题本质**：s1 的所有权转移给了 s2，s1 不再有效

**解决方案**：
- 如果需要两个副本 → `clone()`
- 如果只需要读 → 传递引用 `&s1`
- 如果 s2 只是临时 → 考虑重新设计

### 模式 2：借用冲突

```rust
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // 冲突！
// println!("{}", r1);
```

**问题本质**：不可变借用和可变借用同时存在

**解决方案**：
- 确保可变借用使用完毕后再创建新的借用
- 重新组织代码结构

### 模式 3：生命周期不匹配

```rust
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}
```

**问题本质**：返回值生命周期必须和输入之一关联

**解决关键**：
1. 明确每个引用的生命周期
2. 用命名生命周期表达关系
3. 优先返回 owned 类型

---

## 我的思考流程

### 1. 谁拥有数据？

| 情况 | 所有者 |
|-----|-------|
| 函数参数 | 调用者拥有 |
| 函数内部变量 | 函数拥有（返回时销毁） |
| 结构体字段 | 结构体实例拥有 |
| `Arc<T>` | 多个共享所有者 |

### 2. 借用合理吗？

| 操作 | 借用类型 | 注意事项 |
|-----|---------|---------|
| 只读操作 | `&T` | 可多个同时存在 |
| 需要修改 | `&mut T` | 同时只能有一个 |
| 借用期间原值会被修改吗？ | 如果会，问题就在这里 |

### 3. 能避免生命周期吗？

```
返回 String 而不是 &str
    ↓
使用 owned 集合而不是切片
    ↓
用 Arc/Rc 共享所有权
    ↓
生命周期不是必须品
```

---

## 智能指针选择

| 场景 | 选择 | 原因 |
|-----|------|-----|
| 堆分配单个值 | `Box<T>` | 简单直接 |
| 单线程共享引用计数 | `Rc<T>` | 轻量级 |
| 多线程共享引用计数 | `Arc<T>` | 原子操作 |
| 需要运行时借用检查 | `RefCell<T>` | 单线程内部可变性 |
| 多线程内部可变性 | `Mutex<T>` 或 `RwLock<T>` | 线程安全 |

---

## 反模式警示

| 反模式 | 问题 | 正确做法 |
|-------|------|---------|
| `.clone()` 到处都是 | 隐藏所有权问题 | 思考真正的所有权需求 |
| `'static` 用于所有地方 | 过于宽松且不精确 | 使用实际需要的生命周期 |
| `Box::leak()` 泄漏内存 | 内存浪费 | 使用正确的生命周期管理 |
| 与借用检查器对抗 | 给自己挖坑 | 理解并配合编译器的设计 |

---

## 实践建议

### 新手常见困惑

1. **"什么时候该用引用，什么时候该用所有权？"**
   - 函数参数用引用（除非需要消耗）
   - 函数返回值用引用（如果 caller 不需要所有权）
   - 存储时考虑生命周期复杂度

2. **"生命周期注解到底怎么加？"**
   - 大多数情况编译器能推断
   - 结构体、trait impl、方法返回引用时需要显式标注
   - 生命周期名称要有意义，比如 `'connection`、`'file`

3. **"为什么这个借用不工作？"**
   - 可变借用期间原值不可访问
   - 检查借用的作用域范围
   - 考虑是否需要重新组织代码结构

---

## 错误码速查表

| 错误码 | 含义 | 不要说 | 要问 |
|-------|------|--------|------|
| E0382 | 值被移动后使用 | "clone it" | 谁应该拥有这个数据？ |
| E0597 | 生命周期太短 | "extend lifetime" | 作用域边界正确吗？ |
| E0506 | 借用未结束就被修改 | "end borrow first" | 变异应该在哪里发生？ |
| E0507 | 从引用 move 出数据 | "clone before move" | 为什么要从引用移动？ |
| E0515 | 返回非 owned 数据 | "return owned" | 调用者应该拥有数据吗？ |
| E0716 | 临时值生命周期不足 | "bind to variable" | 为什么这个是临时的？ |
| E0106 | 生命周期参数缺失 | "add 'a" | 生命周期关系是什么？ |

---

## 思考过程

遇到所有权问题时，按以下步骤思考：

1. **这个数据在领域中的角色是什么？**
   - 实体（有唯一标识）→ owned
   - 值对象（可互换）→ clone/copy 可以
   - 临时计算结果 → 考虑重构

2. **所有权设计是有意为之还是意外？**
   - 有意 → 在约束内工作
   - 意外 → 考虑重新设计

3. **修复症状还是重新设计？**
   - 如果尝试 3 次仍失败 → 升级到设计层面

---

## 向上追踪（Trace Up）

当所有权错误持续时，追踪到设计层面：

```
E0382 (moved value)
    ↑ 问：什么设计选择导致了这种所有权模式？
    ↑ 查：这是实体还是值对象？
    ↑ 查：是否有其他约束？

持续 E0382 → rust-resource：应该用 Arc/Rc 共享吗？
持续 E0597 → rust-type-driven：作用域边界正确吗？
E0506/E0507 → rust-mutability：应该用内部可变性吗？
```

---

## 向下实现（Trace Down）

从设计决策到实现：

```
"数据需要不可变共享"
    ↓ 多线程：Arc<T>
    ↓ 单线程：Rc<T>

"数据需要独占所有权"
    ↓ 返回 owned 值

"数据只是临时使用"
    ↓ 在作用域内使用引用

"需要在函数间传递数据"
    ↓ 考虑生命周期或返回 owned
```


