Course contentsShow
← Rust
Lesson 1394 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Pointer Aliasing Rules

You will understand the strict requirements for mutable and immutable pointer aliasing in Rust's memory model.

In this lesson, you will learn how Rust’s strict pointer aliasing rules prevent data races and enable the compiler to perform aggressive optimizations.

The Aliasing Invariant

At the heart of Rust’s memory safety is a simple but powerful rule: You may have either multiple immutable pointers (&T) to a memory location, or exactly one mutable pointer (&mut T), but never both at the same time.

When you enter the world of unsafe Rust and raw pointers (*const T or *mut T), the borrow checker no longer enforces these rules for you. However, the compiler still assumes these invariants hold true during code generation. If you violate them, you invoke Undefined Behavior (UB).

Why the Rules Matter

Consider this scenario: if the compiler knows you hold a mutable reference (&mut T), it assumes no other part of the program can read or write to that specific memory address. Consequently, it may load the value into a CPU register, perform several operations, and only write it back at the very end. If a rogue alias modifies that memory behind the compiler's back, your "cached" register value becomes stale, leading to logic bugs that are notoriously difficult to debug.

Example of a Violation

Even with raw pointers, you must maintain "logical" exclusivity:

let mut data = 10;
let ptr1 = &mut data as *mut i32;
let ptr2 = &data as *const i32;

unsafe {
    // VIOLATION: We have a mutable pointer and an immutable 
    // pointer active simultaneously. Even if we don't 
    // dereference them yet, this violates aliasing rules.
    *ptr1 = 20; 
    println!("{}", *ptr2); 
}

In the eyes of the compiler, once you create a &mut reference, the memory location is "exclusive." If you create a & reference while that mutable pointer exists, you have created an alias that promises the data won't change—yet the mutable pointer is there to potentially change it. The compiler is free to reorder or optimize your code assuming this conflict cannot happen, leading to unpredictable results.

Key Takeaway: Even in unsafe blocks, you must ensure that no mutable pointer is active while any other pointer (mutable or immutable) is accessing the same memory location.