Module 2 • Lesson 8

The Stack and Heap

📚 8 min read 💻 Free Course 🦀 nixus.pro

Stack vs Heap

Understanding stack and heap is key to understanding Rust's ownership model. The stack is fast, fixed-size, LIFO. The heap is dynamic, slower, requires management.

fn main() {
    // Stack: fixed-size, very fast
    let x: i32 = 5;           // 4 bytes on stack
    let arr: [u8; 8] = [0; 8]; // 8 bytes on stack

    // Heap: dynamic size, owns a pointer on stack
    let s = String::from("hello");
    // Stack: ptr (8 bytes) + len (8) + capacity (8) = 24 bytes
    // Heap: 5 bytes for "hello"

    println!("len={} cap={}", s.len(), s.capacity());

    // Box: explicit heap allocation
    let b = Box::new(42);
    println!("boxed: {}", b);
    println!("deref: {}", *b);
    // When b drops, both Box and heap int are freed
}

Move in Memory

fn main() {
    let s1 = String::from("hello");
    // s1 stack: {ptr: 0xA000, len: 5, cap: 5}
    // heap 0xA000: [h,e,l,l,o]

    let s2 = s1;
    // s2 stack: {ptr: 0xA000, len: 5, cap: 5}  <- same ptr!
    // s1 stack: invalidated by compiler
    // heap: unchanged

    // Without this, dropping both s1 and s2 would free 0xA000 twice!
    // Rust prevents this at COMPILE TIME.

    // Clone makes a real copy:
    let s3 = s2.clone();
    // s3: {ptr: 0xB000, len: 5, cap: 5}
    // heap 0xB000: [h,e,l,l,o]  (new allocation)
    println!("{} {}", s2, s3);
}

// RAII: Drop runs automatically
struct FileHandle { name: String }

impl Drop for FileHandle {
    fn drop(&mut self) {
        println!("Closing: {}", self.name);
    }
}

fn main2() {
    let f = FileHandle { name: "data.txt".to_string() };
    println!("Using file");
} // "Closing: data.txt" - printed automatically

🎯 Practice

  1. Create a struct with a Drop impl. Verify cleanup order (reverse of creation)
  2. Use Box<T> to heap-allocate a large array [i64; 10000]
  3. Create a recursive type: enum List { Cons(i32, Box<List>), Nil }

🎉 Key Takeaways