How to Use Phantom Types for Type-Level Programming

Phantom types use zero-sized markers to enforce compile-time constraints and encode state without runtime overhead.

Phantom types in Rust are zero-sized types used to carry type information at compile time without affecting runtime memory layout. You define a newtype wrapper around PhantomData<T> to encode state or constraints that the compiler enforces, preventing invalid operations like using an uninitialized value. This technique is used internally by the compiler, such as in TypedArena<T> to ensure the arena drops its owned instances of T correctly.

use std::marker::PhantomData;

struct Uninitialized<T> {
    _marker: PhantomData<T>,
}

struct Initialized<T> {
    value: T,
    _marker: PhantomData<T>,
}

// The compiler prevents passing Uninitialized where Initialized is expected
fn process<T>(item: Initialized<T>) {
    // Logic here
}