Day 5: 所有权系统
今天是《30天精通Rust语言》系列的第五天。我们将学习Rust最核心的概念——所有权系统(Ownership)。所有权系统是Rust区别于其他语言的根本特征,它通过编译时检查彻底消除了内存安全问题。
2. 所有权规则
Section titled “2. 所有权规则”Rust的所有权系统遵循三条核心规则:
- 每个值都有一个所有者(owner)。
- 每个值同时只能有一个所有者。
- 当所有者离开作用域时,值会被丢弃(drop)。
理解这三条规则是掌握Rust的关键。
3. 变量作用域
Section titled “3. 变量作用域”让我们从作用域开始理解所有权:
fn main() { // s1在声明前不可用 let s1 = String::from("hello"); // s1可用 println!("{}", s1);} // s1离开作用域,被drop,内存释放与C/C++不同,Rust的内存释放是自动的,由所有权系统管理。
4. 移动语义(Move)
Section titled “4. 移动语义(Move)”对于堆分配的数据(如 String),赋值操作会转移所有权:
fn main() { let s1 = String::from("hello"); let s2 = s1; // 所有权从s1移动到s2
// println!("{}", s1); // 编译错误!s1已无效 println!("{}", s2); // 正常工作}对比C++:如果C++中使用 std::move,效果类似,但Rust的移动是默认行为,编译器保证安全。
5. 复制语义(Copy)
Section titled “5. 复制语义(Copy)”对于栈分配的数据(如基本类型),赋值操作会复制数据:
fn main() { let x = 5; let y = x; // x的值复制到y
println!("x = {}, y = {}", x, y); // 正常工作}Rust的基本类型(如 i32、f64、bool、char)默认实现了 Copy trait,可以在赋值时复制而非移动。
6. 函数中的所有权
Section titled “6. 函数中的所有权”将值传递给函数时,所有权也会发生转移:
fn take_ownership(s: String) { println!("{}", s);} // s离开作用域,String被drop
fn main() { let s = String::from("hello"); take_ownership(s); // 所有权转移到函数 // println!("{}", s); // 编译错误!s已无效}7. 返回值与所有权
Section titled “7. 返回值与所有权”函数可以返回值来转移所有权:
fn give_ownership() -> String { String::from("hello")}
fn main() { let s = give_ownership(); println!("{}", s); // 正常工作}8. 借用(Borrowing)预览
Section titled “8. 借用(Borrowing)预览”借用允许你暂时使用一个值而不获取其所有权,用 & 和 &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最重要的特性之一,下一章将详细讨论。
9. Clone与Copy trait
Section titled “9. Clone与Copy trait”当你确实需要复制一个值而不是移动时,可以使用 clone 方法:
fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); // 深拷贝,s1和s2都有效
println!("s1 = {}, s2 = {}", s1, s2);}clone 方法需要显式调用,适用于堆分配的数据。Copy trait用于栈数据,是自动的、零成本的。
10. Drop与析构函数
Section titled “10. Drop与析构函数”当值离开作用域时,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");} // 这里自动调用drop11. 实践练习
Section titled “11. 实践练习”今天的实践任务:
- 编写程序验证
String的移动语义:尝试在移动后访问原变量,观察编译错误。 - 编写程序验证基本类型(
i32)的复制语义:验证移动后原变量仍可访问。 - 编写一个函数接收
String参数,验证所有权转移到函数。 - 使用
clone方法复制一个String,验证两个变量都有效。 - 实现一个带有
Drop实现的自定义结构体,观察其析构函数何时被调用。
12. 常见问题
Section titled “12. 常见问题”问:为什么不自动实现Clone?
答:clone 可能涉及昂贵的深拷贝操作,Rust要求开发者显式调用以避免意外的性能开销。而 Copy 用于简单廉价的栈数据复制,是自动的。
问:所有权的性能开销如何?
答:所有权系统本身没有任何运行时开销。所有检查都在编译时完成,生成的机器码与手动管理内存的C/C++代码无异。
13. 总结
Section titled “13. 总结”今天我们深入学习了Rust的所有权系统:
- 每个值只有一个所有者,离开作用域时值被自动drop。
- 赋值和函数传参会转移堆数据的所有权(move语义)。
- 基本类型自动复制(Copy trait),无需显式克隆。
- 借用(下一章详细讨论)允许临时使用值而不获取所有权。
所有权系统是Rust内存安全的基石,理解它将为后续学习打下坚实基础。明天我们将学习借用与引用。加油!