In this lesson, you will learn how to use Zero-Sized Types (ZSTs) to enforce valid state transitions at compile time, ensuring your state machine is impossible to misuse.
The Concept: Types as States
When building a state machine, a common bug is calling a method when the system isn't ready (e.g., trying to send() a packet before it is connected()). Instead of using runtime checks like if state == State::Connected, we can represent each state as a unique struct with no fields.
Because these structs are ZSTs, they occupy zero bytes of memory. The compiler uses them purely for "type-level bookkeeping."
Implementing Type-Level Transitions
Consider a simple connection state machine:
struct Disconnected;
struct Connected;
struct Socket<State> {
_state: std::marker::PhantomData<State>,
}
impl Socket<Disconnected> {
fn new() -> Self {
Socket { _state: std::marker::PhantomData }
}
fn connect(self) -> Socket<Connected> {
Socket { _state: std::marker::PhantomData }
}
}
impl Socket<Connected> {
fn send(&self, data: &str) {
println!("Sending: {}", data);
}
}
Why This Works
- Zero Memory Overhead: Because
DisconnectedandConnectedhave no data, theSocketstruct remains the exact same size regardless of which state it is in. - Compile-Time Safety: If you try to call
socket.send()while it is in theDisconnectedstate, the Rust compiler will throw an error. Thesendmethod simply doesn't exist forSocket<Disconnected>. - State Consumption: Notice that
connecttakesself(consuming theDisconnectedinstance) and returns a newSocket<Connected>. This is the "State Pattern" enforced by the borrow checker. You cannot accidentally use the old disconnected socket again because it has been moved into the transition.
By shifting the burden of state validation from the CPU to the compiler, we create faster, more reliable code that requires zero runtime branching.
Key Takeaway: Use empty structs and PhantomData to encode your state machine's transitions into the type system, transforming runtime errors into compile-time impossibilities without increasing your memory footprint.