Course contentsShow
← Rust
Lesson 464 of 1,9104. Lifetimes and Advanced OwnershipFree lesson

Memory Layout and Zero-Sized Types

Leverage ZSTs to encode state at compile time with zero performance cost.

In this lesson, you will learn how to use Zero-Sized Types (ZSTs) to transform your type system into a powerful state machine that leaves no trace in the final binary.

Understanding ZSTs

In Rust, a Zero-Sized Type is a struct or enum that occupies exactly zero bytes of memory. Examples include () (the unit type) and empty structs like struct Unlocked;. Because they take up no space, the compiler ignores them during code generation. They exist solely for the benefit of the borrow checker and the type system.

Encoding State at Compile Time

You can leverage ZSTs to enforce valid state transitions. By using these types as phantom markers in a wrapper, you ensure that illegal operations are caught at compile time, preventing runtime errors without paying a "performance tax" for tracking that state.

Consider a simple Lock system:

struct Unlocked;
struct Locked;

struct Resource<State> {
    data: String,
    _state: std::marker::PhantomData<State>,
}

impl Resource<Unlocked> {
    fn new(data: String) -> Self {
        Resource { data, _state: std::marker::PhantomData }
    }

    fn lock(self) -> Resource<Locked> {
        Resource { data: self.data, _state: std::marker::PhantomData }
    }
}

impl Resource<Locked> {
    fn unlock(self) -> Resource<Unlocked> {
        Resource { data: self.data, _state: std::marker::PhantomData }
    }
}

Why This is Powerful

In the example above, Resource<Unlocked> and Resource<Locked> share the same memory layout: only the size of the String. The State parameter is purely a compile-time guide. When you compile this, the _state field vanishes entirely. You get the safety of a full-blown state machine, but the machine code generated is identical to a simple struct that just holds the string.

Because ZSTs have no size, you can pass them around, move them, and change them without ever needing to perform an actual memory write. You are essentially using the compiler to verify your logic while your hardware focuses only on the data that truly matters.

Key Takeaway: Zero-Sized Types act as compile-time constraints that cost nothing at runtime, allowing you to encode complex logic directly into your types without any memory overhead.