回顾:程序的内存分配

程序通过以下两种方式分配内存:

  • 栈:局部变量的连续内存区域。

    • 值在编译时具有已知的固定大小。
    • 速度极快:只需移动一个栈指针。
    • 易于管理:遵循函数调用规则。
    • 优秀的内存局部性。
  • 堆:函数调用之外的值的存储。

    • 值具有动态大小,具体大小需在运行时确定。
    • 比栈稍慢:需要向系统申请空间。
    • 不保证内存局部性。

示例

Creating a String puts fixed-sized metadata on the stack and dynamically sized data, the actual string, on the heap:

fn main() {
    let s1 = String::from("Hello");
}
StackHeaps1capacity5ptrHellolen5
This slide should take about 5 minutes.
  • 指出 String 底层由 Vec 实现,因此它具有容量和长度,如果值可变,则可以通过在堆上重新分配存储空间进行增长。

  • 如果学员提出相关问题,你可以提及我们不仅能使用[系统分配器]在堆上分配底层内存,还能使用 Allocator API 实现自定义分配器

探索更多

We can inspect the memory layout with unsafe Rust. However, you should point out that this is rightfully unsafe!

fn main() {
    let mut s1 = String::from("Hello");
    s1.push(' ');
    s1.push_str("world");
    // DON'T DO THIS AT HOME! For educational purposes only.
    // String provides no guarantees about its layout, so this could lead to
    // undefined behavior.
    unsafe {
        let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
        println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
    }
}