August 3, 20252 min

How does Rust prevent dangling pointer at compile time?

m
mayo

A dangling pointer occurs when a pointer references memory that has already been freed, leading to undefined behavior like crashes or security vulnerabilities. In languages like C/C++, this is a common issue:

int* create_int() {
    int x = 5;  // `x` lives on the stack
    return &x;  // Returns a pointer to `x`...
}  // `x` is destroyed here (dangling pointer returned!)

Rust eliminates dangling pointers at compile time using its ownership model and lifetime system, ensuring memory safety without runtime overhead.

How Rust Prevents Dangling Pointers

Rust uses two key mechanisms to prevent dangling pointers:

1. Ownership + Borrowing Rules

  • Rule: References (&T or &mut T) must not outlive the data they point to.
  • Enforced by: The borrow checker, which tracks variable scopes and ensures references remain valid.

Example: Rejected at Compile Time:

fn dangling() -> &String {  // Missing lifetime specifier!
    let s = String::from("hello");
    &s  // ERROR: `s` dies at end of function
}       // Compiler: "returns a reference to dropped data"

Fixed with Lifetimes (Explicit Guarantee):

fn valid_reference<'a>(s: &'a String) -> &'a String {
    s  // OK: Returned reference tied to input's lifetime
}

2. Lifetime Annotations

  • Rust requires explicit lifetime declarations ('a) when references cross scope boundaries.
  • The compiler ensures all references obey their assigned lifetimes, preventing references to freed memory.

Example: Struct with Reference:

struct Book<'a> {  // Must declare lifetime
    title: &'a str  // Reference must live as long as `Book`
}

fn main() {
    let title = String::from("Rust");
    let book = Book { title: &title };
    // `book.title` cannot outlive `title`
}

Why This Matters

Language Dangling Pointer Risk Safety Mechanism
C/C++ High (manual memory mgmt) None (programmer's responsibility)
Rust Zero Compile-time checks (ownership + lifetimes)

Key Takeaways

✅ Rust’s compiler guarantees:

  • No references to freed memory.
  • No undefined behavior from dangling pointers.
  • Safety without runtime overhead.

Real-World Impact: Crates like hyper (HTTP) and tokio (async) rely on these guarantees for secure, performant code.