Rust中的内存与垃圾回收:无GC语言如何管理资源

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Rust不需要垃圾回收器却依然安全?

\n\n

当习惯了Python、Java或C#的开发者第一次听说Rust时,他们常常因缺少熟悉的垃圾回收器(GC)而感到不安。“怎么会这样?”他们想,“没有GC,我们岂不是要回到手动管理内存、内存泄漏和段错误的时代?”

\n\n

剧透:不,不会回去。Rust提供了第三条道路——所有权(ownership)系统,它在编译阶段就能保证内存安全。没有运行时开销,没有GC,没有悬垂指针。只有清晰的规则和一个不会让你搬起石头砸自己脚的编译器。

\n\n

在本文中,我们将剖析Rust的内存结构、什么是RAII、所有权、借用和生命周期,以及为什么这一切使Rust成为最可靠的系统编程语言之一。

\n\n

Rust中的内存结构:栈与堆

\n\n

与任何底层语言一样,Rust中的内存分为两个主要区域:

\n\n
    \n
  • 栈(stack)——快速但大小有限的内存,遵循后进先出(LIFO)原则。这里存储局部变量、函数参数和返回地址。
  • \n
  • 堆(heap)——速度较慢但更灵活的内存,用于存储编译时大小未知的数据(例如String、Vec、Box)。
  • \n
\n\n

在C++中,你需要自行决定何时分配和释放堆内存(通过new/delete)。在Java或Python中,垃圾回收器会替你完成,但会定期暂停程序。而Rust使用所有权系统,一旦变量离开作用域,内存就会自动释放。

\n\n

示例:基本内存分配

\n\n
fn main() {\n    let s = String::from("你好,世界!"); // 在堆上分配内存\n    println!("{}", s);\n} // 此处s离开作用域,内存被释放
\n\n

注意:没有free()或delete。在代码块结束时,编译器隐式插入析构函数调用——drop()。这被称为RAII(资源获取即初始化)——资源在初始化时获取,在对象销毁时释放。

\n\n

所有权(Ownership):Rust的首要规则

\n\n

Rust的所有权系统基于三条简单规则:

\n\n
    \n
  1. Rust中的每个值都恰好有一个所有者(owner)。
  2. \n
  3. 当所有者离开作用域时,该值被删除。
  4. \n
  5. 所有者可以转移(move)或临时借用(borrow)值。
  6. \n
\n\n

听起来很枯燥?让我们看看它在实践中如何运作。

\n\n

移动(Move)

\n\n
fn main() {\n    let s1 = String::from("hello");\n    let s2 = s1; // s1被移动到s2\n    // println!("{}", s1); // 错误!s1不再拥有数据的所有权\n    println!("{}", s2); // 正确\n}
\n\n

在C++中,这样的赋值会导致两个指针指向同一块内存——进而导致双重释放。而在Rust中,编译器直接禁止在移动后使用s1。没有内存泄漏,没有双重删除。

\n\n

复制(Copy)

\n\n

简单类型,如整数、布尔值和字符,实现了Copy trait。对于它们,赋值会创建完整副本:

\n\n
fn main() {\n    let x = 42;\n    let y = x;\n    println!("x = {}, y = {}", x, y); // 两者都可访问!\n}
\n\n

为什么这样?因为i32存储在栈上,复制它只是复制几个字节。廉价且安全。

\n\n

借用(Borrowing)与引用

\n\n

如果需要临时访问数据而不转移所有权,该怎么办?使用引用。Rust允许通过引用借用值。

\n\n
fn main() {\n    let s = String::from(                

博客

书籍推荐