深入理解 Rust 的内存模型:变量、值与指针
文章目录
- 变量、值与指针
- 与 C++ 对比
- 高层与低层的变量模型
- Rust 高层模型(High-level model)
- Rust 低层模型(Low-level model)
- 与 C++ 对比
- 三大内存区域
- 1. 栈(Stack)
- 2. 堆(Heap)
- 3. 静态内存(Static Memory)
- 小结
Rust 以“内存安全”著称,但想真正写出高效、安全的 Rust 代码,就必须理解它背后的内存模型。本篇文章将带你从 变量、值与指针 的概念出发,逐步理解 栈(stack)、堆(heap)和静态内存(static memory),以及 Rust 的核心机制——所有权、借用和生命周期。同时我们还会结合 C++ 的内存模型 做详细对比,方便已经熟悉 C++ 的读者更快地建立对应理解。
变量、值与指针
在 Rust 中:
- 值(Value):类型与具体数据的组合。例如,
6u8
是一个u8
类型的值,它在内存中的表示是字节0x06
。"Hello world"
是字符串值,它的底层表示是 UTF-8 字节序列。- 这里的概念和C++中的字面值是类似的,而
Hello world
这种字符串,Rust的类型是&str
,和C++中的const char*
是类似的
- 这里的概念和C++中的字面值是类似的,而
- 变量(Variable):是一个“命名的位置”,用来存储值。变量通常存放在栈上。
- 指针(Pointer):保存某个内存地址的值,可以解引用访问其指向的数据。Rust 的引用(
&T
、&mut T
)本质上就是指针。
let x = 42;
let y = 43;
let var1 = &x;
let mut var2 = &x;
var2 = &y;
这里:
- 值有四个:
42
、43
、x
的地址、y
的地址。 - 变量有四个:
x
,y
,var1
,var2
。
与 C++ 对比
在 C++ 中:
-
值:表现形式类似,例如
int x = 42;
中,42
就是值,存放在x
里。 -
变量:C++ 的变量和 Rust 类似,通常存储在栈上。但 C++ 不会在编译器层面禁止未初始化使用,例如:
int a; // 未初始化 std::cout << a; // 未定义行为(UB)
Rust 则会强制报错,避免 UB。
-
指针与引用:
- C++ 引用(
int&
)类似 Rust 的&T
,但 C++ 允许把引用绑定到临时对象上,容易导致悬垂引用;Rust 编译期禁止此类情况。 - C++ 指针(
int*
)则与 Rust 的原始指针(*const T
,*mut T
)接近,需要开发者自己保证安全性。
- C++ 引用(
高层与低层的变量模型
Rust 高层模型(High-level model)
- 把变量看作值的“名字”。
- 值的使用抽象为 数据流(flow),一旦值被移动,数据流断开,变量失效。
- 编译器借用检查器会验证是否存在非法的数据流。
Rust 低层模型(Low-level model)
- 把变量看作一块内存槽。
- 赋值覆盖旧数据,引用则是内存的地址。
与 C++ 对比
- C++ 更接近低层模型:变量就是一块内存,指针/引用直接操作地址。编译器不会阻止危险操作。
- 在高层抽象上,C++ 缺乏 Rust 那样的“所有权”和“借用检查器”。程序员需要依靠编码规范、智能指针(
std::unique_ptr
,std::shared_ptr
)或工具(如 ASan, Valgrind)来避免内存错误。
示例对比:
int* p = nullptr;
*p = 42; // 未定义行为:空指针解引用
Rust 中:
let p: *mut i32 = std::ptr::null_mut();
unsafe {*p = 42; // 编译器要求 unsafe 块,显式提示风险
}
Rust 把危险操作显式隔离,C++ 则允许直接执行。
三大内存区域
1. 栈(Stack)
-
Rust:函数调用分配栈帧,局部变量生命周期受作用域限制。
-
C++:相同机制,但 C++ 不会禁止返回局部变量的引用:
int& foo() {int x = 42;return x; // 返回悬垂引用,UB }
Rust:
fn foo() -> &i32 {let x = 42;&x // 编译错误:借用不满足生命周期 }
2. 堆(Heap)
-
Rust:通过
Box<T>
、Vec<T>
等安全抽象分配,释放由所有权系统自动管理。 -
C++:需要手动
new/delete
,或使用智能指针:auto p = std::make_unique<int>(42); // 自动释放
Rust 的
Box<T>
类似unique_ptr
,但由编译器强制约束,避免误用。
3. 静态内存(Static Memory)
- Rust:
static
变量在程序整个运行期存在,'static
生命周期显式建模。 - C++:全局变量、静态变量在整个程序运行期间存在,但缺乏生命周期建模,容易导致初始化顺序问题(static initialization order fiasco)。
小结
Rust 的内存安全性来自于编译期的严格规则,而 C++ 则更多依赖程序员经验:
特性 | Rust | C++ |
---|---|---|
未初始化变量 | 编译时报错 | 运行时 UB |
指针安全 | 引用/借用受检查,原始指针需 unsafe | 任意指针操作,易出错 |
所有权 | 强制存在,编译器跟踪 | 无,需靠约定或智能指针 |
生命周期 | 类型系统显式建模 | 无,需人工推理 |
内存回收 | RAII + 编译器保证 | RAII,但需谨慎设计 |
可以看到,Rust 在很多地方对 C++ 进行了“强制收紧”,牺牲部分灵活性换取编译期的安全性。对于熟悉 C++ 的开发者,可以把 Rust 看作是“有更强类型约束和更严格规则的现代 C++”。