所有权(Ownership)
学习目标
- 理解 Rust 的所有权规则
- 理解 Move 语义
- 理解栈和堆的区别
- 掌握 Clone 和 Copy 的区别
核心概念
所有权三条规则
1. Rust 中每个值都有一个所有者(owner)
2. 同一时刻只能有一个所有者
3. 所有者离开作用域时,值被自动丢弃(drop)
栈和堆
栈(Stack) 堆(Heap)
┌──────────┐ ┌──────────┐
│ 快速分配 │ │ 慢速分配 │
│ 固定大小 │ │ 动态大小 │
│ 自动释放 │ │ 手动管理 │
│ i32, bool│ │ String │
│ 原始类型 │ │ Vec │
└──────────┘ └──────────┘
Move 语义
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}", s1); // ❌ 编译错误:s1 已失效
println!("{}", s2); // ✅ s2 是新的所有者
}
为什么需要 Move?
s1 (栈) 堆内存
┌──────────┐ ┌──────────┐
│ ptr ────────→ │ "hello" │
│ len: 5 │ └──────────┘
│ cap: 5 │
└──────────┘
s2 = s1 之后:
s1 (已失效) s2 (栈) 堆内存
┌──────────┐ ┌──────────┐ ┌──────────┐
│ ✗ 无效 │ │ ptr ────────→ │ "hello" │
└──────────┘ │ len: 5 │ └──────────┘
│ cap: 5 │
└──────────┘
如果 s1 和 s2 都指向同一块堆内存,两个都离开作用域时会 double free。Move 避免了这个问题。
Clone(深拷贝)
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝:堆上的数据也复制了
println!("s1 = {}, s2 = {}", s1, s2); // ✅ 都有效
}
Copy trait(栈上类型自动复制)
fn main() {
let x = 5;
let y = x; // i32 实现了 Copy,自动复制
println!("x = {}, y = {}", x, y); // ✅ 都有效
}
实现了 Copy 的类型:
- 所有整数类型(i32, u64 等)
- 布尔类型 bool
- 浮点类型 f32, f64
- 字符类型 char
- 元组(如果所有元素都是 Copy):
(i32, f64)是 Copy
没有实现 Copy 的类型:
- String
- Vec<T>
- 任何需要堆分配的类型
函数与所有权
fn take_ownership(s: String) {
println!("{}", s);
} // s 离开作用域,被 drop
fn make_copy(x: i32) {
println!("{}", x);
} // x 离开作用域,但 i32 是 Copy,不影响调用者
fn main() {
let s = String::from("hello");
take_ownership(s); // s 的所有权移动到函数内
// println!("{}", s); // ❌ s 已失效
let x = 5;
make_copy(x); // x 复制了一份传进去
println!("{}", x); // ✅ x 仍然有效
}
返回值与所有权
fn give_string() -> String {
let s = String::from("hello");
s // 所有权转移给调用者
}
fn take_and_give_back(s: String) -> String {
println!("{}", s);
s // 还给调用者
}
fn main() {
let s1 = give_string();
let s2 = take_and_give_back(s1);
// println!("{}", s1); // ❌ 已移动
println!("{}", s2); // ✅
}
实践练习
练习 1:理解 Move
fn main() {
let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1); // 这行会报什么错?试试看
let s3 = s2.clone();
println!("s2 = {}, s3 = {}", s2, s3); // ✅
}
练习 2:函数所有权转移
fn print_string(s: String) {
println!("{}", s);
}
fn main() {
let s = String::from("hello");
print_string(s);
// 如何让 s 继续可用?提示:clone 或返回
}
练习 3:判断 Copy 还是 Move
fn main() {
// 以下哪些会报错?为什么?
// 1)
let a = 42;
let b = a;
println!("{}, {}", a, b);
// 2)
let s1 = String::from("hi");
let s2 = s1;
// println!("{}, {}", s1, s2);
// 3)
let t1 = (1, 2.0, true);
let t2 = t1;
println!("{:?}, {:?}", t1, t2);
// 4)
let v1 = vec![1, 2, 3];
let v2 = v1;
// println!("{:?}, {:?}", v1, v2);
}
常见错误
1. 使用已移动的值
let s = String::from("hello");
let s2 = s;
println!("{}", s); // ❌ value used here after move
2. 误以为赋值是复制
let v = vec![1, 2, 3];
let v2 = v; // 这是 Move,不是复制!
// v 已失效
3. 在循环中移动
let s = String::from("hello");
for _ in 0..3 {
// println!("{}", s); // ❌ 第二次循环时 s 已被移动
}
// ✅ 应该用 &s(借用)或 s.clone()
小结
| 概念 | 说明 |
|---|---|
| 所有权 | 每个值只有一个所有者 |
| Move | 赋值/传参时所有权转移,原变量失效 |
| Clone | 显式深拷贝 |
| Copy | 栈上类型自动复制,原变量仍有效 |
| Drop | 离开作用域时自动释放 |
一句话:默认是 Move,想复制要显式调用 .clone()。