Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

English Original

理解所有权

你将学到什么: Rust 的所有权系统,以及为什么 let s2 = s1 会让 s1 失效(这不同于 C# 的引用复制);三条所有权规则、CopyMove 类型的区别、使用 &&mut 的借用方式,以及借用检查器如何替代垃圾回收机制。

难度: 中级

所有权是 Rust 最独特的特性,也是 C# 开发者在思维方式上需要完成的最大转变。


C# 与 Rust 内存模型对比

C# 内存模型

在 C# 中,所有的类(对象)都存储在堆(Heap)上。当你将一个变量赋值给另一个变量时,你实际上只是复制了引用(指针)。此时,两个变量都指向同一个对象。

var original = new List<int> { 1, 2, 3 };
var reference = original; // 两个变量指向同一份数据 (List)
original.Add(4);
Console.WriteLine(reference.Count); // 结果为 4 - 它们是同一个对象

Rust 所有权模型

在 Rust 中,每个值都有且仅有一个确定的所有者。当你将一个非 Copy 的值赋值给一个新的变量时,其所有权会发生转移(Moved)

#![allow(unused)]
fn main() {
let original = vec![1, 2, 3];
let moved = original; // 所有权正式转移给 'moved'
// println!("{:?}", original); // ❌ 编译报错:original 已不再拥有该数据
}

三条所有权规则

  1. 每个值都有一个所有者。
  2. 每个值在任一时刻有且仅有一个所有者。
  3. 当所有者离开其作用域时,该值将被释放。(确定性的内存清理)

Copy 类型 vs Move 类型

  • Copy 类型:结构简单的数值类型,如整数 (i32)、布尔值 (bool) 和浮点数 (f64)。这些类型的赋值是直接复制,因为它们在栈上的复制开销极低。
  • Move 类型:管理堆内存的复杂类型,如 StringVec<T>。这些类型在赋值时默认发生移动,以此来防止“双重释放(double-free)”等内存错误。

借用(Borrowing)基础

借用允许你在不夺取所有权的情况下访问数据。

不可变借用 (&)

你可以同时拥有任意多个不可变借用(引用)。

#![allow(unused)]
fn main() {
fn read_value(value: &Vec<i32>) {
    println!("长度:{}", value.len());
}
}

可变借用 (&mut)

在任一时刻,你只能拥有一个可变借用(引用)。这种设计可以在编译期就彻底杜绝数据竞争。

#![allow(unused)]
fn main() {
fn modify_value(value: &mut Vec<i32>) {
    value.push(42);
}
}

借用检查器规则

  1. 允许多个不可变借用。
  2. 同一时刻仅允许一个可变借用。
  3. 在存在可变借用期间,不允许存在任何不可变借用。

RAII 与 垃圾回收 (GC)

在 C# 中,垃圾回收器 (GC) 会周期性地扫描堆内内存并清理不再被引用的对象。而在 Rust 中,一旦所有者离开作用域,内存就会被立即清理。这被称为 RAII(资源获取即初始化)。

#![allow(unused)]
fn main() {
{
    let file = std::fs::File::open("data.txt")?;
    // 当 'file' 离开作用域时,文件句柄会自动关闭并释放。
    // 无需 'using' 语句或手动调用 'Dispose()'。
}
}

核心洞见: Rust 通过所有权系统,在没有运行时 GC 开销的情况下,实现了完美的自动化内存管理。