所有权(Ownership)是 Rust 最独特、最核心的特性,它让 Rust 无需垃圾回收器即可保证内存安全。理解所有权是掌握 Rust 的关键。

1. 所有权规则

Rust 的所有权系统基于三条核心规则:

规则一:Rust 中的每一个值都有一个变量作为它的所有者(owner)。

规则二:在同一时刻,值有且只有一个所有者

规则三:当所有者离开作用域(scope)时,该值将被丢弃(drop)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 规则一:每个值都有一个所有者
let s = String::from("hello"); // s 是 "hello" 的所有者

// 规则二:同一时刻只有一个所有者
let s2 = s; // 所有权转移给 s2,s 不再是所有者
// println!("{}", s); // 编译错误!s 已失效
println!("{}", s2); // s2 是当前所有者

// 规则三:所有者离开作用域时,值被丢弃
{
let s3 = String::from("world"); // s3 进入作用域
println!("{}", s3);
} // s3 离开作用域,"world" 的内存被释放(调用 drop)

// println!("{}", s3); // 编译错误!s3 已不存在
}

作用域与 Drop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
println!("=== 作用域演示 ===");

{ // s 还未声明,不可用
let s = String::from("hello"); // s 从此刻开始可用
println!("内部作用域: {}", s); // 使用 s
} // 作用域结束,s 被 drop,内存释放

// 嵌套作用域
let outer = String::from("outer");
{
let inner = String::from("inner");
println!("外部: {}, 内部: {}", outer, inner);
} // inner 被 drop
println!("外部仍然有效: {}", outer);
// println!("{}", inner); // 编译错误!inner 已不存在
}

2. 栈与堆

理解栈(Stack)和堆(Heap)对于理解所有权至关重要。

2.1 概念说明

1
2
3
4
5
6
7
8
9
10
11
12
栈(Stack)                          堆(Heap)
┌──────────────┐ ┌────────────────────┐
│ 后进先出(LIFO) │ │ 动态分配,大小不固定 │
│ 分配速度快 │ │ 分配速度慢 │
│ 大小编译时确定 │ │ 大小运行时确定 │
│ │ │ │
│ 存储的数据: │ │ 存储的数据: │
│ - 基本类型 │ 指针指向→ │ - String 的内容 │
│ - 固定大小数据 │ ─────────────→ │ - Vec 的元素 │
│ - 引用/指针 │ │ - Box 包装的数据 │
│ - 函数调用帧 │ │ - 动态大小的数据 │
└──────────────┘ └────────────────────┘

2.2 栈上数据 vs 堆上数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
// ========== 栈上数据(固定大小,Copy 语义) ==========
let x: i32 = 42; // i32 存在栈上(4字节)
let y = x; // 值的副本,x 和 y 都有效
println!("栈上数据: x={}, y={}", x, y); // 都可以使用

let a: f64 = 3.14; // f64 存在栈上(8字节)
let b = a; // 值的副本
println!("栈上数据: a={}, b={}", a, b);

let flag: bool = true; // bool 存在栈上(1字节)
let flag2 = flag; // 值的副本
println!("栈上数据: flag={}, flag2={}", flag, flag2);

// ========== 堆上数据(动态大小,Move 语义) ==========
let s1 = String::from("hello");
// String 的内部结构:
// 栈上:{ ptr: 指向堆的指针, len: 5, capacity: 5 }
// 堆上:['h', 'e', 'l', 'l', 'o']

let s2 = s1; // 所有权移动!s1 不再有效
// println!("{}", s1); // 编译错误!
println!("堆上数据: s2={}", s2);

// Vec 也是堆上数据
let v1 = vec![1, 2, 3];
let v2 = v1; // 所有权移动
// println!("{:?}", v1); // 编译错误!
println!("Vec: {:?}", v2);
}

3. 移动语义(Move)

3.1 基本类型的 Copy

基本类型(标量类型)实现了 Copy trait,赋值时会自动复制值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
// 整数 - 实现了 Copy
let x = 5;
let y = x; // x 的值被复制到 y
println!("x={}, y={}", x, y); // 两者都有效

// 浮点数 - 实现了 Copy
let a = 3.14;
let b = a;
println!("a={}, b={}", a, b);

// 布尔 - 实现了 Copy
let t = true;
let f = t;
println!("t={}, f={}", t, f);

// 字符 - 实现了 Copy
let c1 = 'A';
let c2 = c1;
println!("c1={}, c2={}", c1, c2);

// 元组(如果所有元素都实现了 Copy)- 也实现了 Copy
let tup = (1, 2.0, true);
let tup2 = tup;
println!("tup={:?}, tup2={:?}", tup, tup2);

// 固定大小数组(如果元素实现了 Copy)- 也实现了 Copy
let arr = [1, 2, 3];
let arr2 = arr;
println!("arr={:?}, arr2={:?}", arr, arr2);
}

3.2 String 等堆上数据的移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
fn main() {
// String 没有实现 Copy,赋值时发生移动
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2

// s1 已经失效,不能再使用
// println!("{}", s1);
// 编译错误:
// error[E0382]: borrow of moved value: `s1`
// --> src/main.rs:7:20
// |
// | let s1 = String::from("hello");
// | -- move occurs because `s1` has type `String`,
// | which does not implement the `Copy` trait
// | let s2 = s1;
// | -- value moved here
// | println!("{}", s1);
// | ^^ value borrowed here after move

println!("s2 = {}", s2); // 正常

// Vec 也是移动语义
let v1 = vec![1, 2, 3];
let v2 = v1;
// println!("{:?}", v1); // 编译错误!v1 已移动
println!("v2 = {:?}", v2);

// Box 也是移动语义
let b1 = Box::new(42);
let b2 = b1;
// println!("{}", b1); // 编译错误!
println!("b2 = {}", b2);
}

3.3 函数参数的所有权转移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fn main() {
let s = String::from("hello");

// 将 s 传给函数,所有权转移到函数参数
takes_ownership(s);

// s 已经无效,不能再使用
// println!("{}", s);
// 编译错误:value borrowed here after move

// 基本类型传递时是 Copy
let x = 5;
makes_copy(x);
println!("x 仍然有效: {}", x); // x 仍然可用

println!("---");

// 如果需要在传递后继续使用,可以先克隆
let s2 = String::from("world");
takes_ownership(s2.clone()); // 传递克隆体
println!("s2 仍然有效: {}", s2); // s2 仍然可用
}

fn takes_ownership(some_string: String) {
// some_string 接收了所有权
println!("函数内: {}", some_string);
} // some_string 离开作用域并被 drop

fn makes_copy(some_integer: i32) {
// some_integer 是 x 的副本
println!("函数内: {}", some_integer);
} // some_integer 离开作用域,但不影响外部的 x

3.4 函数返回值的所有权转移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fn main() {
// 函数返回值也会转移所有权
let s1 = gives_ownership();
println!("s1 = {}", s1);

let s2 = String::from("hello");

// 将 s2 传入函数,函数处理后返回,所有权回到调用者
let s3 = takes_and_gives_back(s2);
// println!("{}", s2); // 编译错误!s2 已移动
println!("s3 = {}", s3);

// 返回多个值(使用元组)
let s4 = String::from("hello");
let (s5, len) = calculate_length(s4);
println!("'{}' 的长度是 {}", s5, len);
}

fn gives_ownership() -> String {
let some_string = String::from("yours");
some_string // 返回值的所有权转移给调用者
}

fn takes_and_gives_back(a_string: String) -> String {
println!("处理: {}", a_string);
a_string // 返回所有权
}

fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length) // 通过元组返回所有权和计算结果
}

4. Copy trait 与 Clone trait

4.1 Copy trait

Copy trait 使类型在赋值时自动进行按位复制(浅复制),原变量仍然有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
fn main() {
// ========== 实现了 Copy 的类型 ==========
// 所有整数类型: i8, i16, i32, i64, i128, isize
// u8, u16, u32, u64, u128, usize
let a: i32 = 42;
let b = a;
println!("i32 Copy: a={}, b={}", a, b);

// 所有浮点类型: f32, f64
let c: f64 = 3.14;
let d = c;
println!("f64 Copy: c={}, d={}", c, d);

// 布尔类型: bool
let e = true;
let f = e;
println!("bool Copy: e={}, f={}", e, f);

// 字符类型: char
let g = 'A';
let h = g;
println!("char Copy: g={}, h={}", g, h);

// 元组(当所有元素都实现 Copy 时)
let i = (1, 2.0, true);
let j = i;
println!("Tuple Copy: i={:?}, j={:?}", i, j);

// 数组(当元素实现 Copy 时)
let k = [1, 2, 3];
let l = k;
println!("Array Copy: k={:?}, l={:?}", k, l);

// 引用类型 (&T) 也实现了 Copy
let s = String::from("hello");
let r1 = &s;
let r2 = r1; // 引用被复制(不是值被复制)
println!("Ref Copy: r1={}, r2={}", r1, r2);

// ========== 没有实现 Copy 的类型 ==========
// String, Vec<T>, Box<T>, HashMap<K,V>, 等堆上数据

// 自定义类型可以通过 derive 实现 Copy
#[derive(Debug, Copy, Clone)]
struct Point {
x: f64,
y: f64,
}

let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1; // Copy!
println!("Point Copy: p1={:?}, p2={:?}", p1, p2);

// 注意:如果结构体中包含不能 Copy 的字段,整个结构体不能 Copy
// #[derive(Copy, Clone)] // 编译错误!
// struct Person {
// name: String, // String 不能 Copy
// age: u32,
// }
}

4.2 Clone trait

Clone 提供显式的深复制能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
// Clone 进行深复制(堆上数据也会被复制)
let s1 = String::from("hello");
let s2 = s1.clone(); // 显式克隆,s1 和 s2 都有效

println!("s1 = {}", s1); // 有效
println!("s2 = {}", s2); // 有效
println!("s1 == s2: {}", s1 == s2); // true,内容相同

// 验证是深复制(两个独立的堆内存)
let mut s3 = s1.clone();
s3.push_str(" world");
println!("s1 = {}", s1); // "hello" (未改变)
println!("s3 = {}", s3); // "hello world"

// Vec 的 Clone
let v1 = vec![1, 2, 3];
let v2 = v1.clone();
println!("v1 = {:?}", v1);
println!("v2 = {:?}", v2);

// 嵌套结构的深复制
let v3 = vec![
String::from("hello"),
String::from("world"),
];
let v4 = v3.clone(); // Vec 和内部的 String 都被复制
println!("v3 = {:?}", v3);
println!("v4 = {:?}", v4);
}

4.3 Copy 与 Clone 的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Copy 是 Clone 的子 trait
// 实现 Copy 的类型必须也实现 Clone
// Copy 是隐式浅复制(编译器自动调用)
// Clone 是显式深复制(需要手动调用 .clone())

#[derive(Debug, Clone)] // 只实现 Clone(不能 Copy,因为有 String)
struct Student {
name: String,
score: f64,
}

#[derive(Debug, Copy, Clone)] // 同时实现 Copy 和 Clone
struct Coordinate {
x: f64,
y: f64,
}

fn main() {
// Student 只能 Clone
let s1 = Student {
name: String::from("张三"),
score: 95.5,
};
let s2 = s1.clone(); // 显式克隆
// let s3 = s1; // 这会移动 s1,之后 s1 不再可用
println!("s1 = {:?}", s1);
println!("s2 = {:?}", s2);

// Coordinate 可以 Copy
let c1 = Coordinate { x: 1.0, y: 2.0 };
let c2 = c1; // 自动 Copy
let c3 = c1; // 仍然可以使用 c1
println!("c1 = {:?}", c1);
println!("c2 = {:?}", c2);
println!("c3 = {:?}", c3);
}

5. 引用与借用

引用(Reference)允许你使用值而不获取其所有权,这称为”借用”(Borrowing)。

5.1 不可变引用(&T)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let s1 = String::from("hello");

// & 创建一个引用,它"借用"了 s1 的值
let len = calculate_length(&s1); // 传递引用,不转移所有权

// s1 仍然有效!
println!("'{}' 的长度是 {}", s1, len);

// 可以同时有多个不可变引用
let r1 = &s1;
let r2 = &s1;
let r3 = &s1;
println!("r1={}, r2={}, r3={}", r1, r2, r3); // 完全没问题
}

fn calculate_length(s: &String) -> usize {
// s 是 String 的引用,不拥有所有权
s.len()

// 不能修改借用的值
// s.push_str(" world"); // 编译错误!不可变引用不能修改
} // s 离开作用域,但因为它不拥有所有权,所以什么也不会发生

5.2 可变引用(&mut T)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fn main() {
let mut s = String::from("hello");

// &mut 创建可变引用
change(&mut s);
println!("修改后: {}", s); // "hello, world"

// 可变引用的限制:同一时刻只能有一个可变引用
let r1 = &mut s;
// let r2 = &mut s; // 编译错误!不能同时有两个可变引用
// error[E0499]: cannot borrow `s` as mutable more than once at a time
println!("r1 = {}", r1);

// 但不同作用域中可以有多个可变引用
{
let r2 = &mut s;
r2.push_str("!");
println!("内部 r2 = {}", r2);
} // r2 离开作用域

let r3 = &mut s; // 可以了,因为 r2 已经不存在
r3.push_str("!");
println!("r3 = {}", r3);
}

fn change(some_string: &mut String) {
some_string.push_str(", world");
}

5.3 引用规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
fn main() {
let mut s = String::from("hello");

// 规则:在任意给定时间,你可以拥有以下之一(但不能同时拥有):
// - 任意数量的不可变引用(&T)
// - 恰好一个可变引用(&mut T)

// ===== 合法的情况 =====

// 1. 多个不可变引用
{
let r1 = &s;
let r2 = &s;
println!("多个不可变引用: r1={}, r2={}", r1, r2);
}

// 2. 一个可变引用
{
let r = &mut s;
r.push_str(" world");
println!("可变引用: r={}", r);
}

// ===== 非法的情况 =====

// 3. 不能同时有不可变引用和可变引用
// let r1 = &s;
// let r2 = &mut s; // 编译错误!
// println!("{}, {}", r1, r2);
// error[E0502]: cannot borrow `s` as mutable because it is
// also borrowed as immutable

// ===== NLL(Non-Lexical Lifetimes)=====
// Rust 2018+ 引入的 NLL 使借用检查更智能
// 引用的生命周期在最后一次使用后就结束

let r1 = &s;
let r2 = &s;
println!("r1={}, r2={}", r1, r2);
// r1 和 r2 在此之后不再使用,所以生命周期在这里结束

let r3 = &mut s; // 这是合法的!因为 r1 和 r2 已经不再使用
r3.push_str("!");
println!("r3={}", r3);
}

5.4 悬垂引用(Dangling Reference)

Rust 在编译期防止悬垂引用的出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
// Rust 防止悬垂引用
// let reference_to_nothing = dangle();

// 正确的做法:返回所有权而非引用
let s = no_dangle();
println!("s = {}", s);
}

// 这个函数会编译错误!
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 返回 s 的引用
// } // s 离开作用域被释放,引用指向无效内存!
// 编译错误:
// error[E0106]: missing lifetime specifier
// this function's return type contains a borrowed value,
// but there is no value for it to be borrowed from

// 正确做法:直接返回 String(转移所有权)
fn no_dangle() -> String {
let s = String::from("hello");
s // 所有权转移给调用者
}

6. 切片(Slice)

切片是对集合中一段连续元素的引用,没有所有权。

6.1 字符串切片(&str)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fn main() {
let s = String::from("hello world");

// 字符串切片:&s[开始索引..结束索引]
// 注意:索引是字节位置,不是字符位置
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"
println!("hello = '{}', world = '{}'", hello, world);

// 语法糖
let hello2 = &s[..5]; // 从开头开始可以省略 0
let world2 = &s[6..]; // 到结尾可以省略最后的索引
let whole = &s[..]; // 整个字符串的切片
println!("hello2='{}', world2='{}', whole='{}'", hello2, world2, whole);

// &str 就是字符串切片类型
let s_literal: &str = "hello world"; // 字符串字面量就是 &str
let s_slice: &str = &s[..]; // String 的切片也是 &str

println!("字面量: {}", s_literal);
println!("切片: {}", s_slice);

// 使用切片作为函数参数(推荐)
let word = first_word(&s);
println!("第一个单词: {}", word);

// 字符串字面量也可以直接传入
let word2 = first_word("hello rust");
println!("第一个单词: {}", word2);
}

// 接受 &str 而不是 &String,这样更通用
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();

for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' {
return &s[..i];
}
}

s // 没有空格,返回整个字符串
}

6.2 切片与所有权的安全性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn main() {
let mut s = String::from("hello world");

let word = first_word(&s); // word 是 s 的不可变借用

// s.clear(); // 编译错误!
// error[E0502]: cannot borrow `s` as mutable because it is
// also borrowed as immutable
// clear() 需要可变引用,但 word 持有不可变引用

println!("第一个单词: {}", word);

// word 使用完毕后,可以修改 s
s.clear();
println!("清空后: '{}'", s);
}

fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' {
return &s[..i];
}
}
s
}

6.3 注意 UTF-8 字符串切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let s = String::from("你好世界");

// 中文字符在 UTF-8 中占 3 个字节
println!("字符串字节长度: {}", s.len()); // 12
println!("字符数: {}", s.chars().count()); // 4

// 正确的切片(按字节边界)
let hello = &s[0..6]; // "你好"(每个中文字符 3 字节)
println!("前两个字: {}", hello);

// 错误的切片(不在字符边界上)会 panic
// let bad = &s[0..4]; // panic: byte index 4 is not a char boundary

// 安全的方式:使用 chars() 迭代器
let first_two: String = s.chars().take(2).collect();
println!("前两个字符: {}", first_two);

// 按字符索引的安全方式
if let Some(c) = s.chars().nth(2) {
println!("第三个字符: {}", c); // '世'
}
}

6.4 数组切片(&[T])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
fn main() {
let arr = [1, 2, 3, 4, 5];

// 数组切片
let slice: &[i32] = &arr[1..3]; // [2, 3]
println!("数组切片: {:?}", slice);

let first_three = &arr[..3]; // [1, 2, 3]
let last_two = &arr[3..]; // [4, 5]
let all = &arr[..]; // [1, 2, 3, 4, 5]

println!("前三个: {:?}", first_three);
println!("后两个: {:?}", last_two);
println!("全部: {:?}", all);

// 切片的常用方法
println!("切片长度: {}", slice.len());
println!("切片是否为空: {}", slice.is_empty());
println!("第一个元素: {:?}", slice.first());
println!("最后一个元素: {:?}", slice.last());
println!("包含 3: {}", slice.contains(&3));

// Vec 的切片
let v = vec![10, 20, 30, 40, 50];
let v_slice = &v[1..4]; // [20, 30, 40]
println!("Vec 切片: {:?}", v_slice);

// 可变切片
let mut arr2 = [5, 4, 3, 2, 1];
let slice_mut = &mut arr2[..];
slice_mut.sort();
println!("排序后: {:?}", arr2);

// 使用切片作为函数参数
let sum = sum_slice(&arr[..]);
println!("数组元素之和: {}", sum);

let sum2 = sum_slice(&v[1..4]);
println!("Vec 部分元素之和: {}", sum2);
}

// 函数接受切片,适用于数组和 Vec
fn sum_slice(slice: &[i32]) -> i32 {
let mut total = 0;
for &item in slice {
total += item;
}
total
}

7. 所有权与函数

7.1 所有权转移模式总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
fn main() {
// ====== 模式1:函数获取所有权 ======
let s = String::from("hello");
consume(s);
// println!("{}", s); // 编译错误!所有权已转移

// ====== 模式2:函数借用(不可变引用) ======
let s2 = String::from("world");
borrow(&s2);
println!("借用后仍然有效: {}", s2); // OK

// ====== 模式3:函数可变借用 ======
let mut s3 = String::from("hello");
mutate(&mut s3);
println!("修改后: {}", s3); // "hello!!!"

// ====== 模式4:函数返回所有权 ======
let s4 = create();
println!("创建的字符串: {}", s4);

// ====== 模式5:获取所有权后返回 ======
let s5 = String::from("hello");
let s5 = process(s5); // 获取所有权,处理后返回
println!("处理后: {}", s5);
}

fn consume(s: String) {
println!("消费: {}", s);
} // s 被 drop

fn borrow(s: &String) {
println!("借用: {}", s);
} // 借用结束,不影响原值

fn mutate(s: &mut String) {
s.push_str("!!!");
}

fn create() -> String {
String::from("new string")
}

fn process(mut s: String) -> String {
s.push_str(" processed");
s
}

7.2 选择传参方式的指导原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fn main() {
let data = String::from("important data");

// 1. 只需要读取 -> 使用不可变引用 &T
print_data(&data);
println!("数据仍有效: {}", data);

// 2. 需要修改 -> 使用可变引用 &mut T
let mut data = data;
append_data(&mut data);
println!("修改后: {}", data);

// 3. 需要获取所有权(如存储或转移) -> 按值传递 T
store_data(data);
// data 已经被转移

// 4. 处理切片 -> 使用 &str 而不是 &String
let s = String::from("hello world");
let word = find_word(&s); // String -> &str 自动转换
let word2 = find_word("rust"); // &str 直接传入
println!("word={}, word2={}", word, word2);
}

fn print_data(data: &String) {
println!("[只读] {}", data);
}

fn append_data(data: &mut String) {
data.push_str(" (modified)");
}

fn store_data(data: String) {
println!("[存储] {}", data);
// data 的所有权转移到这里,函数结束时被 drop
}

fn find_word(s: &str) -> &str {
&s[..s.find(' ').unwrap_or(s.len())]
}

8. 所有权与结构体

8.1 结构体拥有自己字段的所有权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#[derive(Debug)]
struct User {
username: String, // 结构体拥有 String 的所有权
email: String,
active: bool,
sign_in_count: u64,
}

fn main() {
let user1 = User {
username: String::from("张三"),
email: String::from("zhangsan@example.com"),
active: true,
sign_in_count: 1,
};

println!("用户: {:?}", user1);

// 结构体更新语法会移动字段的所有权
let user2 = User {
email: String::from("lisi@example.com"),
..user1 // 使用 user1 的其余字段
};
// user1 中的 username 已经移动到 user2
// user1 的 active 和 sign_in_count 是 Copy 的,所以仍然可以使用这些字段
// 但 user1 整体不能再使用(因为 username 已移动)

// println!("{:?}", user1); // 编译错误!
// println!("{}", user1.username); // 编译错误!已移动
println!("user1.active = {}", user1.active); // OK,bool 是 Copy
println!("user2 = {:?}", user2);
}

8.2 结构体中使用引用(需要生命周期标注)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 结构体中使用引用需要生命周期标注
#[derive(Debug)]
struct Excerpt<'a> {
part: &'a str, // 'a 是生命周期参数
}

fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence;

{
let first_period = novel.find('.').unwrap_or(novel.len());
first_sentence = Excerpt {
part: &novel[..first_period],
};
println!("摘录: {:?}", first_sentence);
}

// first_sentence 仍然有效,因为 novel 的生命周期够长
println!("仍然有效: {}", first_sentence.part);

// 但如果引用的数据生命周期不够长,会编译错误:
// let excerpt;
// {
// let short_lived = String::from("short");
// excerpt = Excerpt { part: &short_lived };
// } // short_lived 被 drop
// println!("{}", excerpt.part); // 编译错误!悬垂引用
}

8.3 结构体方法中的所有权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#[derive(Debug)]
struct Rectangle {
width: f64,
height: f64,
}

impl Rectangle {
// &self - 不可变借用,最常用
fn area(&self) -> f64 {
self.width * self.height
}

// &mut self - 可变借用,需要修改结构体
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}

// self - 获取所有权,调用后原值不再可用
fn into_square(self) -> Rectangle {
let side = (self.width + self.height) / 2.0;
Rectangle {
width: side,
height: side,
}
}

// 关联函数(不接受 self)
fn new(width: f64, height: f64) -> Rectangle {
Rectangle { width, height }
}
}

fn main() {
let mut rect = Rectangle::new(10.0, 5.0);

// &self 方法 - rect 仍然可用
println!("面积: {}", rect.area());
println!("rect 仍然有效: {:?}", rect);

// &mut self 方法 - 修改 rect
rect.scale(2.0);
println!("缩放后: {:?}", rect);

// self 方法 - rect 被消费
let square = rect.into_square();
// println!("{:?}", rect); // 编译错误!rect 已被移动
println!("正方形: {:?}", square);
}

9. 常见所有权问题与解决方案

9.1 问题:在循环中使用移动过的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fn main() {
// 错误示例:
// let s = String::from("hello");
// for _ in 0..3 {
// print_string(s); // 第一次循环后 s 已经移动
// // 编译错误:use of moved value: `s`
// }

// 解决方案1:使用引用
let s = String::from("hello");
for _ in 0..3 {
print_string_ref(&s); // 借用,不转移所有权
}
println!("循环后 s 仍然有效: {}", s);

println!();

// 解决方案2:使用 clone
let s2 = String::from("world");
for _ in 0..3 {
print_string(s2.clone()); // 每次传递克隆体
}
println!("循环后 s2 仍然有效: {}", s2);
}

fn print_string(s: String) {
println!("所有权版: {}", s);
}

fn print_string_ref(s: &String) {
println!("引用版: {}", s);
}

9.2 问题:从集合中取出元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fn main() {
let names = vec![
String::from("Alice"),
String::from("Bob"),
String::from("Charlie"),
];

// 错误:直接索引会尝试移动
// let first = names[0]; // 编译错误!
// error[E0507]: cannot move out of index of `Vec<String>`

// 解决方案1:使用引用
let first = &names[0];
println!("第一个(引用): {}", first);

// 解决方案2:使用 clone
let first_owned = names[0].clone();
println!("第一个(克隆): {}", first_owned);

// 解决方案3:使用 get 方法
if let Some(name) = names.get(0) {
println!("第一个(get): {}", name);
}

// 解决方案4:如果确实需要移出元素
let mut names2 = vec![
String::from("Alice"),
String::from("Bob"),
];
let removed = names2.remove(0); // 移出第一个元素
println!("移出: {}", removed);
println!("剩余: {:?}", names2);

// 解决方案5:使用 swap_remove(更高效,但改变顺序)
let mut names3 = vec![
String::from("Alice"),
String::from("Bob"),
String::from("Charlie"),
];
let removed = names3.swap_remove(0);
println!("swap_remove: {}", removed);
println!("剩余: {:?}", names3); // ["Charlie", "Bob"]
}

9.3 问题:结构体部分字段移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#[derive(Debug)]
struct Config {
name: String,
value: String,
count: u32,
}

fn main() {
let config = Config {
name: String::from("timeout"),
value: String::from("30"),
count: 5,
};

// 部分移动
let name = config.name; // name 字段被移动

// config 整体不再可用
// println!("{:?}", config); // 编译错误!

// 但未移动的字段仍可单独访问
println!("value: {}", config.value); // OK
println!("count: {}", config.count); // OK(Copy 类型)
println!("name: {}", name);

// 解决方案:使用引用访问字段
let config2 = Config {
name: String::from("host"),
value: String::from("localhost"),
count: 1,
};

let name_ref = &config2.name; // 借用,不移动
println!("config2: {:?}", config2); // OK,整体仍然可用
println!("name_ref: {}", name_ref);
}

9.4 问题:闭包捕获变量的所有权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
let name = String::from("Rust");

// 闭包默认按引用捕获
let greet = || println!("Hello, {}!", name);
greet();
println!("name 仍然有效: {}", name); // OK

// 使用 move 强制转移所有权
let name2 = String::from("World");
let greet2 = move || println!("Hello, {}!", name2);
greet2();
// println!("{}", name2); // 编译错误!name2 已被移动到闭包中

// 解决方案:在 move 前 clone
let name3 = String::from("Everyone");
let name3_clone = name3.clone();
let greet3 = move || println!("Hello, {}!", name3_clone);
greet3();
println!("name3 仍然有效: {}", name3); // OK
}

10. 所有权的内存模型图解

10.1 栈上数据(Copy 语义)

1
2
3
4
5
6
7
8
9
let x: i32 = 42;
let y = x; // Copy

栈内存:
┌─────────┐
│ x: 42 │ ← x 仍然有效
├─────────┤
│ y: 42 │ ← y 是 x 的副本
└─────────┘
1
2
3
4
5
6
fn main() {
let x: i32 = 42;
let y = x;
// 两者独立,互不影响
println!("x={}, y={}", x, y);
}

10.2 堆上数据(Move 语义)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let s1 = String::from("hello");

栈内存: 堆内存:
┌──────────────┐ ┌───┬───┬───┬───┬───┐
│ s1 │ │ h │ e │ l │ l │ o │
│ ptr ────────┼──→ └───┴───┴───┴───┴───┘
│ len: 5 │
│ capacity: 5 │
└──────────────┘

let s2 = s1; // Move!

栈内存: 堆内存:
┌──────────────┐
│ s1 (无效!) │ ┌───┬───┬───┬───┬───┐
│ ptr ──── X │ │ h │ e │ l │ l │ o │
│ len: 5 │ └───┴───┴───┴───┴───┘
│ capacity: 5 │ ↑
└──────────────┘ │
┌──────────────┐ │
│ s2 │ │
│ ptr ────────┼────────────────┘
│ len: 5 │
│ capacity: 5 │
└──────────────┘
1
2
3
4
5
6
fn main() {
let s1 = String::from("hello");
let s2 = s1; // Move: s1 -> s2
// s1 已失效
println!("s2 = {}", s2);
}

10.3 Clone(深复制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let s1 = String::from("hello");
let s2 = s1.clone();

栈内存: 堆内存:
┌──────────────┐ ┌───┬───┬───┬───┬───┐
│ s1 │ │ h │ e │ l │ l │ o │ ← s1 的数据
│ ptr ────────┼──→ └───┴───┴───┴───┴───┘
│ len: 5 │
│ capacity: 5 │
└──────────────┘
┌──────────────┐ ┌───┬───┬───┬───┬───┐
│ s2 │ │ h │ e │ l │ l │ o │ ← s2 的数据(独立副本)
│ ptr ────────┼──→ └───┴───┴───┴───┴───┘
│ len: 5 │
│ capacity: 5 │
└──────────────┘
1
2
3
4
5
6
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深复制
// 两者独立
println!("s1 = {}, s2 = {}", s1, s2);
}

10.4 引用(借用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let s = String::from("hello");
let r = &s;

栈内存: 堆内存:
┌──────────────┐ ┌───┬───┬───┬───┬───┐
│ s │ │ h │ e │ l │ l │ o │
│ ptr ────────┼──→ └───┴───┴───┴───┴───┘
│ len: 5 │ ↑
│ capacity: 5 │ │(通过 s 间接指向)
└──────────────┘ │
┌──────────────┐ │
│ r: &String │ │
│ ptr ────────┼──→ s ───┘
└──────────────┘

r 是 s 的引用(指向 s 的指针),不拥有数据
1
2
3
4
5
6
7
fn main() {
let s = String::from("hello");
let r = &s; // r 借用 s
println!("s = {}", s); // s 仍然有效
println!("r = {}", r); // r 可以读取 s 的值
println!("*r = {}", *r); // 解引用也可以
}

10.5 切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];

栈内存: 堆内存:
┌──────────────┐ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ s │ │ h │ e │ l │ l │ o │ │ w │ o │ r │ l │ d │
│ ptr ────────┼──→ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
│ len: 11 │ ↑ ↑
│ capacity:11 │ │ │
└──────────────┘ │ │
┌──────────────┐ │ │
│ hello: &str │ │ │
│ ptr ────────┼─────┘ │
│ len: 5 │ │
└──────────────┘ │
┌──────────────┐ │
│ world: &str │ │
│ ptr ────────┼─────────────────────────────┘
│ len: 5 │
└──────────────┘
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];

println!("s = '{}'", s);
println!("hello = '{}'", hello);
println!("world = '{}'", world);

// 所有切片都是 s 的借用,s 仍然拥有数据
}

总结

所有权是 Rust 的核心特性,通过以下机制保证内存安全:

  1. 三大规则:每个值有一个所有者,同时只能有一个所有者,所有者离开作用域时值被丢弃
  2. 移动语义:堆上数据赋值时转移所有权,防止双重释放
  3. Copy/Clone:基本类型自动 Copy,复杂类型显式 Clone
  4. 借用规则:任意多个不可变引用 或 恰好一个可变引用,防止数据竞争
  5. 悬垂引用:编译器阻止引用指向已释放的数据
  6. 切片:对数据的部分引用,无所有权,安全地访问集合的一部分

掌握这些概念后,你将能够编写既安全又高效的 Rust 代码。下一章我们将学习控制流程。