讲动人的故事,写懂人的代码
在公司内部的Rust培训课上,讲师贾克强比较了 Rust、Java 和 C++ 三种编程语言在变量越过作用域时自动释放堆内存的不同特性。
Rust 通过所有权系统和借用检查,实现了内存安全和自动管理,从而避免了大部分内存泄漏。Rust 自动管理标准库中数据类型(如 Box
、Vec
、String
)的堆内存,并在这些类型的变量离开作用域时自动释放内存,即使程序员未显式编写清理堆内存的代码。
只有当程序员实现自定义的数据类型,并且该类型拥有需要手动管理的资源时,才需要在 drop
函数中编写清理代码。如果在这种情况下忘记了编写清理代码,确实可能导致资源泄漏,包括但不限于内存泄漏。
相比之下,Java 主要由垃圾回收器(GC)控制内存管理,而 C++ 则需要程序员通过构造函数和析构函数手动控制内存的分配和释放。
席双嘉提出问题:“我对Rust中的字符串变量在超出作用域时自动释放内存的机制非常感兴趣。但如何能够通过代码实例来验证这一点呢?”
贾克强说这是一个好问题,可以作为今天的作业。他请对这个问题感兴趣的同学,在课下找AI编程助手小艾来完成这个作业。
赵可菲对这个问题颇感兴趣。在小艾的帮助下,她迅速完成了代码编写并且成功运行。为了让Rust新手能够理解,她请小艾在代码中的每一行关键语句前加上了注释。此外,她还在main
函数后添加了这个程序的运行结果输出,如代码清单1-1所示。
代码清单1-1 验证当字符串变量超出范围时,Rust会自动调用该变量的drop函数
// 使用 jemallocator 库中的 Jemalloc 内存分配器
use jemallocator::Jemalloc;
// 用属性(用于为代码的特定部分提供元信息的注释)定义一个全局的内存分配器,使用 Jemalloc 作为系统的全局内存分配器
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
fn main() {
{
// 进入一个新的作用域,作用域是用大括号 `{}` 包围的代码块
// 创建一个包含 100M 大字符串的自定义结构体
let _large_string_owner = LargeStringOwner::new(100_000_000); // 100 MB
// 打印创建大字符串后消息
println!("Large string created.");
} // 这里作用域结束,`large_string_owner` 变量自动销毁,`drop` 函数被调用
// 打印离开作用域后的消息
println!("Large string scope ended.");
}
// 该程序运行后的输出为:
// Large string created.
// Dropping LargeStringOwner, releasing large string memory.
// Large string scope ended.
// 自定义一个包含大字符串的结构体,并实现 Drop trait
struct LargeStringOwner {
// 包含一个字符串字段,但允许未使用(避免编译器警告)
#[allow(dead_code)]
content: String,
}
impl LargeStringOwner {
// 为结构体实现一个新的构造函数,接受字符串大小作为参数