第一章:所有权系统——Rust 的灵魂

第一章:所有权系统——Rust 的灵魂

所有权是 Rust 最独特、最重要的特性。它让 Rust 在没有垃圾回收(GC)的情况下保证内存安全。理解所有权,Rust 的其他一切都会变得清晰。


一、为什么需要所有权

内存管理的历史困境:

C/C++ 手动管理内存:
  malloc/free, new/delete
  问题:忘记释放 = 内存泄漏;释放后继续使用 = 悬空指针;释放两次 = 崩溃
  
Java/Python/Go 垃圾回收:
  自动管理,程序员不担心
  问题:GC 暂停(stop-the-world),内存开销大,延迟不可预测
  
Rust 的方案:
  所有权系统(编译时检查)
  没有 GC,没有手动管理,没有运行时开销
  内存安全问题在编译时被捕获,不是运行时崩溃

二、所有权三规则

规则 1:每个值有且只有一个"所有者"(owner)
规则 2:同一时刻,只能有一个所有者
规则 3:当所有者离开作用域,值被自动丢弃(drop)
fn main() {
    // s1 是 "hello" 这个字符串的所有者
    let s1 = String::from("hello");
    
    // 所有权 Move 到 s2
    // s1 不再有效!
    let s2 = s1;
    
    // 编译错误:s1 已经被 move 了
    // println!("{}", s1);  // ❌ 这行会报错
    
    println!("{}", s2);     // ✓ 正确
}   // s2 在这里离开作用域,字符串被自动释放(调用 drop)

三、Move vs Copy

// Copy 类型:基本数据类型存在栈上,赋值时复制值
let x = 5;
let y = x;  // x 和 y 都有效,因为 i32 实现了 Copy trait
println!("{} {}", x, y);  // 都能用

// Move 类型:堆上数据(String, Vec 等),赋值时转移所有权
let s1 = String::from("hello");
let s2 = s1;  // s1 被 move 了
// println!("{}", s1);  // ❌ 报错

// 哪些类型是 Copy 的?
// i8, i16, i32, i64, i128, isize
// u8, u16, u32, u64, u128, usize
// f32, f64
// bool, char
// 元组(如果所有元素都是 Copy 的):(i32, i32) 是 Copy,但 (i32, String) 不是

四、Clone:显式深拷贝

let s1 = String::from("hello");
let s2 = s1.clone();  // 显式深拷贝,s1 仍然有效

println!("s1 = {}, s2 = {}", s1, s2);  // 都能用

// 注意:clone() 有性能开销(复制堆上数据)
// 只在真正需要两个独立拥有的值时使用

五、函数调用与所有权

fn take_ownership(s: String) {
    println!("{}", s);
}   // s 在这里被 drop

fn makes_copy(x: i32) {
    println!("{}", x);
}   // x 只是数值,没有 drop 操作

fn main() {
    let s = String::from("hello");
    take_ownership(s);  // s 的所有权 move 到函数中
    // println!("{}", s);  // ❌ s 已经无效
    
    let x = 5;
    makes_copy(x);      // i32 是 Copy,x 仍然有效
    println!("{}", x);  // ✓
}
// 函数返回值可以转移所有权
fn give_ownership() -> String {
    let s = String::from("hello");
    s   // 所有权 move 给调用者
}

fn take_and_give_back(s: String) -> String {
    s   // 所有权转移出去
}

fn main() {
    let s1 = give_ownership();              // s1 拥有返回值
    let s2 = String::from("world");
    let s3 = take_and_give_back(s2);        // s2 move 进去,s3 拿到返回值
    
    // s2 无效(已被 move)
    println!("{} {}", s1, s3);
}

六、引用(Borrowing)入门

// 传引用,不转移所有权
fn calculate_length(s: &String) -> usize {
    s.len()  // 使用引用,但不拥有它
}

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // &s1 创建 s1 的引用
    
    println!("'{}' 的长度是 {}", s1, len);  // s1 仍然有效!
}
// 可变引用:允许修改借用的值
fn change(s: &mut String) {
    s.push_str(", world");
}

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);  // "hello, world"
}

七、所有权的实际意义(和 C 对比)

// Rust 中永远不可能发生的错误:

// 1. 悬空指针(Dangling Pointer)
// 下面代码在 C 中会是 bug,在 Rust 中编译时报错:
// fn dangle() -> &String {  // 返回引用,但原值会被 drop
//     let s = String::from("hello");
//     &s  // ❌ s 会在函数结束时被 drop,引用变悬空
// }
// Rust 会报错:borrowed value does not live long enough

// 2. 使用已释放的内存(Use After Free)
// 不可能,因为所有权规则保证值还活着时才能使用

// 3. 双重释放(Double Free)
// 不可能,每个值只有一个所有者,离开作用域只释放一次

// 这些保证在编译时给出,不是运行时——意味着零运行时开销

八、常见编译错误与解决方案

// ❌ 错误 1:use of moved value
let s = String::from("hello");
let s2 = s;
println!("{}", s);  // error[E0382]: borrow of moved value: `s`

// ✓ 解决:clone 或使用引用
let s = String::from("hello");
let s2 = s.clone();  // 或者:let s2 = &s;
println!("{}", s);  // 现在 ok

// ❌ 错误 2:cannot borrow as mutable
let s = String::from("hello");
s.push_str(" world");  // error[E0596]: cannot borrow `s` as mutable

// ✓ 解决:声明 mut
let mut s = String::from("hello");
s.push_str(" world");  // ok

// ❌ 错误 3:cannot have multiple mutable references
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;  // error[E0499]: cannot borrow `s` as mutable more than once
println!("{} {}", r1, r2);

// ✓ 解决:串行使用,不同时持有
let mut s = String::from("hello");
{
    let r1 = &mut s;
    println!("{}", r1);
}  // r1 在这里离开作用域
let r2 = &mut s;  // ok,r1 已经结束了
println!("{}", r2);

关键认知

所有权的直觉

把内存资源想象成一把钥匙。Move 就是把钥匙交给别人(你不再有)。Clone 就是复制一把新钥匙。& 借用就是让别人暂时用你的钥匙,但你一直都有。&mut 可变借用就是借出钥匙,同时承诺期间你不会自己用(保证独占)。

编译器是你的朋友

Rust 的编译错误信息非常详细,通常包含:

  • 错误原因
  • 出错位置
  • 建议如何修复

学 Rust 的关键:读完整的错误信息,不要只看第一行。

“Rust 的所有权系统不是限制,是约束。就像安全带不会让你开车开得更慢,但在事故时会救你的命。所有权规则让你的代码不可能出现整类的 Bug。”