目录
思维导图
1. 所有权的定义
所有权是一套规则,用于管理Rust程序如何使用内存。Rust通过所有权系统来管理内存,编译器会检查这些规则的遵守情况。违反任何规则将导致程序无法编译。所有权的特点是不会影响程序的运行速度。
1.1 所有权的重要性
- 所有权是Rust语言的核心概念,理解这一点是掌握Rust的基础。
- 随着对所有权规则的熟悉,编写安全高效的代码将变得更加自然。
2. 栈与堆
Rust的内存管理涉及栈和堆的概念。
2.1 栈
- 栈是按后进先出(LIFO)原则存储数据的结构,存储的数据必须具有已知的固定大小。
- 数据在栈上添加称为“推入”,移除称为“弹出”。
2.2 堆
- 堆是一个不太有序的内存区域,数据的存储位置由内存分配器动态确定。
- 在堆上分配内存的过程比栈上慢,因为需要查找足够大的空闲空间。
3. 所有权规则
Rust的所有权规则包括:
- 每个值都有一个所有者。
- 每次只能有一个所有者。
- 当所有者超出作用域时,值将被释放。
4. 变量作用域
- 变量的作用域是指在程序中有效的范围。
- 变量在声明后有效,直到超出作用域。
5. String类型
Rust提供了String类型来处理动态大小的文本数据。
5.1 String的创建与操作
- 可以通过
String::from
函数创建String。 - String是可变的,可以使用
push_str
方法追加字符串。
5.2 String与字符串字面量的区别
- 字符串字面量是不可变的,适合已知大小的文本。
- String允许在运行时动态分配内存,适合用户输入等情况。
6. 内存与分配
- Rust通过所有权系统自动管理内存,避免了手动分配和释放的复杂性。
- 当变量超出作用域时,Rust会自动调用
drop
函数释放内存。
7. 移动与复制
- 在Rust中,赋值操作可能导致数据的移动或复制。
- 对于简单数据类型(如整数),Rust会进行复制,保持原变量有效。
- 对于String类型,赋值操作会移动所有权,原变量将失效。
7.1 移动示例
- 当执行
let s2 = s1;
时,s1的所有权转移到s2,s1不再有效,示例如下: -
fn main() { let s1 = String::from("hello"); let s2 = s1; // s1 的值被移动到 s2 println!("{s1}, world!"); // 这里会报错,因为 s1 不再有效 }
7.2 复制示例
- 对于整数类型
let x = 5; let y = x;
,x仍然有效,因为整数实现了Copy特性,如图。 -
fn main() { let x = 5; let y = x; println!("x = {x}, y = {y}"); //output: x = 5, y = 5 }
7.3 clone示例
- 想要深度复制
String
的堆数据,我们可以使用一个名为clone
的常用方法,如图。 -
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {s1}, s2 = {s2}"); //output:s1 = hello, s2 = hello }
8. 函数与所有权
- 将值传递给函数时,会移动或复制所有权。
- 函数返回值时也会转移所有权。
8.1 函数传递示例
consume_string(s),
将s的所有权转移到函数内,如图。-
fn main() { let s = String::from("hello"); // s 进入作用域 consume_string(s); // s 的值移动到函数中 // println!("{s}"); // 这里会报错,因为 s 不再有效 } fn consume_string(input_string: String) { // input_string 进入作用域 println!("{input_string}"); // input_string 结束作用域时,内存被释放 }
8.2 函数转移所有权示例
- 对于实现了Copy特性的类型,仍然可以在函数调用后使用原变量。
-
fn main() { let x = 5; // x 进入作用域 print_integer(x); // x 会传递给函数, // 但由于 i32 是 Copy 类型,x 仍然可以继续使用 } // 这里,x 作用域。 fn print_integer(value: i32) { // value 进入作用域 println!("{value}"); // 打印 value 的值 } // 这里,value 离开作用域。由于是基本类型,不会发生特殊操作。
8.3 函数转移所有权示例
- Rust允许通过返回值转移所有权,避免了冗余的参数传递,如图。
-
fn main() { let s1 = create_string(); // create_string 创建并返回一个字符串,移动到 s1 let s2 = String::from("hello"); // s2 进入作用域 let s3 = transfer_ownership(s2); // s2 移动到函数,返回值移动到 s3 } fn create_string() -> String { let some_string = String::from("yours"); // some_string 进入作用域 some_string // 返回 some_string,移动到调用函数 } fn transfer_ownership(a_string: String) -> String { a_string // 返回 a_string,移动到调用函数 }
8.4 元组
- 可以使用元组返回多个值,简化函数的设计。
-
fn main() { let s1 = String::from("hello"); let (s2, len) = get_length_and_string(s1); // 返回一个元组 println!("The length of '{s2}' is {len}."); } fn get_length_and_string(input: String) -> (String, usize) { let length = input.len(); // 获取字符串长度 (input, length) // 返回字符串和长度 }
9. 引用
- Rust支持引用机制,可以在不转移所有权的情况下使用值。
- 引用使得函数可以使用值而不改变所有权,从而提高了灵活性。
tips: 实现了Copy特征的类型:
- 所有整数类型,例如i16。
- 布尔类型,
bool
,其值为true
和false
。 - 所有浮点类型,例如
f64
。 - 字符类型,
char
。 - 元组,如果它们只包含也实现了
Copy
的类型。例如,(i32, u32)
实现了Copy
,但(i32, String)
没有。