所有权概念 #
一、所有权概述 #
所有权是Rust最独特的特性,它让Rust无需垃圾回收器就能保证内存安全。
1.1 所有权规则 #
Rust的三条核心所有权规则:
- Rust中的每个值都有一个所有者
- 同一时刻只能有一个所有者
- 当所有者离开作用域,值将被丢弃
rust
fn main() {
{
let s = String::from("hello"); // s 进入作用域
// 使用 s
println!("{}", s);
} // s 离开作用域,内存被释放
// println!("{}", s); // 错误:s 已失效
}
二、栈与堆 #
2.1 栈数据 #
基本类型存储在栈上,大小固定:
rust
fn main() {
let x = 5;
let y = x; // 复制值
println!("x = {}, y = {}", x, y); // 两者都有效
}
2.2 堆数据 #
String 等类型存储在堆上:
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // 移动所有权
// println!("{}", s1); // 错误:s1 已失效
println!("{}", s2); // 正确
}
2.3 String 内部结构 #
text
String 结构:
┌─────────────┬─────────────┬─────────────┐
│ ptr │ len │ capacity │
│ (指针) │ (长度) │ (容量) │
└─────────────┴─────────────┴─────────────┘
│
▼
┌───┬───┬───┬───┬───┐
│ h │ e │ l │ l │ o │ 堆上的数据
└───┴───┴───┴───┴───┘
三、移动语义 #
3.1 移动 #
当值被赋给另一个变量时,所有权被转移:
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}", s1); // 错误:value borrowed here after move
println!("{}", s2);
}
3.2 函数传参 #
将值传递给函数也会转移所有权:
rust
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有权移动到函数
// println!("{}", s); // 错误:s 已失效
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string 离开作用域,内存被释放
3.3 函数返回值 #
返回值也会转移所有权:
rust
fn main() {
let s1 = gives_ownership(); // 函数将所有权移动给 s1
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2); // s2 移动到函数,函数返回值移动给 s3
println!("s1 = {}", s1);
println!("s3 = {}", s3);
// println!("s2 = {}", s2); // 错误:s2 已失效
}
fn gives_ownership() -> String {
String::from("yours")
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
四、克隆 #
4.1 clone 方法 #
使用 clone 创建深拷贝:
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝
println!("s1 = {}", s1);
println!("s2 = {}", s2); // 两者都有效
}
4.2 克隆的代价 #
克隆会复制堆上的数据,可能开销较大:
rust
fn main() {
let s1 = String::from("hello");
// 克隆前
println!("克隆前: {}", s1);
let s2 = s1.clone();
// 克隆后两者都有效
println!("s1: {}, s2: {}", s1, s2);
}
五、Copy trait #
5.1 Copy 类型 #
实现了 Copy trait 的类型在赋值时会复制而不是移动:
rust
fn main() {
let x = 5;
let y = x; // 复制,不是移动
println!("x = {}, y = {}", x, y); // 两者都有效
}
5.2 哪些类型实现了 Copy #
- 所有整数类型:
i32,u32,i64等 - 浮点类型:
f32,f64 - 布尔类型:
bool - 字符类型:
char - 元组(如果所有元素都是 Copy 类型):
(i32, i32) - 不可变引用:
&T
rust
fn main() {
// 整数
let a: i32 = 5;
let b = a;
println!("a: {}, b: {}", a, b);
// 浮点
let c: f64 = 3.14;
let d = c;
println!("c: {}, d: {}", c, d);
// 布尔
let e: bool = true;
let f = e;
println!("e: {}, f: {}", e, f);
// 元组
let g = (1, 2, 3);
let h = g;
println!("g: {:?}, h: {:?}", g, h);
// 不可变引用
let s = String::from("hello");
let r1 = &s;
let r2 = r1; // 复制引用
println!("r1: {}, r2: {}", r1, r2);
}
5.3 不能 Copy 的情况 #
rust
fn main() {
// String 没有 Copy
let s1 = String::from("hello");
// let s2 = s1; // 移动
// println!("{}", s1); // 错误
// 可变引用没有 Copy
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = r1; // 错误:可变引用不能 Copy
println!("{}", r1);
}
六、所有权与函数 #
6.1 传入函数 #
rust
fn main() {
let s = String::from("hello");
print_string(s.clone()); // 克隆后传入
println!("仍然有效: {}", s);
print_string(s); // 移动所有权
// println!("{}", s); // 错误:s 已失效
}
fn print_string(s: String) {
println!("{}", s);
}
6.2 返回所有权 #
rust
fn main() {
let s1 = String::from("hello");
let s2 = take_and_return(s1);
println!("s2 = {}", s2);
}
fn take_and_return(s: String) -> String {
println!("收到: {}", s);
s // 返回所有权
}
6.3 多返回值 #
rust
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("字符串 '{}' 的长度是 {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
七、作用域与 Drop #
7.1 自动释放 #
当变量离开作用域时,Rust自动调用 drop 方法:
rust
struct CustomData {
data: String,
}
impl Drop for CustomData {
fn drop(&mut self) {
println!("释放 CustomData: {}", self.data);
}
}
fn main() {
let c = CustomData { data: String::from("test") };
println!("使用 CustomData");
} // c 离开作用域,自动调用 drop
7.2 提前释放 #
使用 std::mem::drop 提前释放:
rust
fn main() {
let s = String::from("hello");
println!("{}", s);
drop(s); // 提前释放
// println!("{}", s); // 错误:s 已被释放
println!("s 已被释放");
}
八、部分移动 #
8.1 解构时的部分移动 #
rust
fn main() {
let tuple = (String::from("hello"), 5);
let (s, n) = tuple; // 解构
// println!("{:?}", tuple); // 错误:tuple.0 已移动
println!("s = {}", s);
println!("n = {}", n); // n 是 Copy 类型,仍然有效
}
8.2 数组元素的部分移动 #
rust
fn main() {
let arr = [String::from("a"), String::from("b")];
let s = arr[0].clone(); // 克隆
println!("arr[0] = {}, s = {}", arr[0], s);
let s2 = arr[1]; // 移动
// println!("arr[1] = {}", arr[1]); // 错误
println!("s2 = {}", s2);
}
九、所有权图解 #
9.1 移动语义 #
text
移动前:
s1 ──→ [ptr|len|cap]
│
▼
[h|e|l|l|o]
移动后:
s1 (无效)
s2 ──→ [ptr|len|cap]
│
▼
[h|e|l|l|o]
9.2 克隆语义 #
text
克隆前:
s1 ──→ [ptr|len|cap]
│
▼
[h|e|l|l|o]
克隆后:
s1 ──→ [ptr|len|cap] s2 ──→ [ptr|len|cap]
│ │
▼ ▼
[h|e|l|l|o] [h|e|l|l|o]
十、实践示例 #
10.1 字符串处理 #
rust
fn process_string(s: String) -> String {
let mut result = s;
result.push_str(" processed");
result
}
fn main() {
let s1 = String::from("hello");
let s2 = process_string(s1);
println!("处理结果: {}", s2);
}
10.2 结构体所有权 #
rust
struct User {
username: String,
email: String,
active: bool,
}
fn main() {
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
active: true,
};
// username 和 email 的所有权属于 user
println!("用户: {}, 邮箱: {}", user.username, user.email);
// 移动 username
let username = user.username;
println!("用户名: {}", username);
// println!("用户: {}", user.username); // 错误:已移动
// active 是 Copy 类型
let active = user.active;
println!("活跃状态: {} {}", active, user.active);
}
十一、总结 #
本章学习了:
- 所有权的三条核心规则
- 栈与堆的区别
- 移动语义
- 克隆与深拷贝
- Copy trait
- 函数与所有权
- 作用域与 Drop
所有权是Rust内存安全的核心机制。下一章,我们将学习引用与借用。
最后更新:2026-03-27