Skip to content

Day 5: 所有权系统

今天是《30天精通Rust语言》系列的第五天。我们将学习Rust最核心的概念——所有权系统(Ownership)。所有权系统是Rust区别于其他语言的根本特征,它通过编译时检查彻底消除了内存安全问题。

Rust的所有权系统遵循三条核心规则:

  1. 每个值都有一个所有者(owner)。
  2. 每个值同时只能有一个所有者。
  3. 当所有者离开作用域时,值会被丢弃(drop)。

理解这三条规则是掌握Rust的关键。

让我们从作用域开始理解所有权:

fn main() {
// s1在声明前不可用
let s1 = String::from("hello"); // s1可用
println!("{}", s1);
} // s1离开作用域,被drop,内存释放

与C/C++不同,Rust的内存释放是自动的,由所有权系统管理。

对于堆分配的数据(如 String),赋值操作会转移所有权:

fn main() {
let s1 = String::from("hello");
let s2 = s1; // 所有权从s1移动到s2
// println!("{}", s1); // 编译错误!s1已无效
println!("{}", s2); // 正常工作
}

对比C++:如果C++中使用 std::move,效果类似,但Rust的移动是默认行为,编译器保证安全。

对于栈分配的数据(如基本类型),赋值操作会复制数据:

fn main() {
let x = 5;
let y = x; // x的值复制到y
println!("x = {}, y = {}", x, y); // 正常工作
}

Rust的基本类型(如 i32、f64、bool、char)默认实现了 Copy trait,可以在赋值时复制而非移动。

将值传递给函数时,所有权也会发生转移:

fn take_ownership(s: String) {
println!("{}", s);
} // s离开作用域,String被drop
fn main() {
let s = String::from("hello");
take_ownership(s); // 所有权转移到函数
// println!("{}", s); // 编译错误!s已无效
}

函数可以返回值来转移所有权:

fn give_ownership() -> String {
String::from("hello")
}
fn main() {
let s = give_ownership();
println!("{}", s); // 正常工作
}

借用允许你暂时使用一个值而不获取其所有权,用 & 和 &mut 表示:

fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 借用s1
println!("Length of '{}' is {}", s1, len); // s1仍然有效
}

借用是Rust最重要的特性之一,下一章将详细讨论。

当你确实需要复制一个值而不是移动时,可以使用 clone 方法:

fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝,s1和s2都有效
println!("s1 = {}, s2 = {}", s1, s2);
}

clone 方法需要显式调用,适用于堆分配的数据。Copy trait用于栈数据,是自动的、零成本的。

当值离开作用域时,Rust会自动调用 drop 函数进行清理:

struct Custom {
data: String,
}
impl Drop for Custom {
fn drop(&mut self) {
println!("Dropping Custom with data: {}", self.data);
}
}
fn main() {
let c = Custom { data: String::from("hello") };
println!("Created Custom");
} // 这里自动调用drop

今天的实践任务:

  1. 编写程序验证 String 的移动语义:尝试在移动后访问原变量,观察编译错误。
  2. 编写程序验证基本类型(i32)的复制语义:验证移动后原变量仍可访问。
  3. 编写一个函数接收 String 参数,验证所有权转移到函数。
  4. 使用 clone 方法复制一个 String,验证两个变量都有效。
  5. 实现一个带有 Drop 实现的自定义结构体,观察其析构函数何时被调用。

问:为什么不自动实现Clone?

答:clone 可能涉及昂贵的深拷贝操作,Rust要求开发者显式调用以避免意外的性能开销。而 Copy 用于简单廉价的栈数据复制,是自动的。

问:所有权的性能开销如何?

答:所有权系统本身没有任何运行时开销。所有检查都在编译时完成,生成的机器码与手动管理内存的C/C++代码无异。

今天我们深入学习了Rust的所有权系统:

  • 每个值只有一个所有者,离开作用域时值被自动drop。
  • 赋值和函数传参会转移堆数据的所有权(move语义)。
  • 基本类型自动复制(Copy trait),无需显式克隆。
  • 借用(下一章详细讨论)允许临时使用值而不获取所有权。

所有权系统是Rust内存安全的基石,理解它将为后续学习打下坚实基础。明天我们将学习借用与引用。加油!