liveBook · Manning

Type state

As explained in Write Powerful Rust Macros

The type state pattern encodes an object's permitted states in its type so invalid state transitions or configurations are rejected at compile time.

Type state encodes an object’s current permitted state in a type parameter. Instead of checking transitions at run time, Rust exposes only the methods valid for that parameter, so the compiler rejects impossible calls. section 8.5.1

Model a traffic light as types

Use marker types such as `Green` and `Red`, plus a marker trait. `TrafficLight<Green>` implements `turn_red`, returning `TrafficLight<Red>`; `TrafficLight<Red>` implements only `turn_green`. `PhantomData<T>` records the otherwise unused state parameter. A second `turn_red` is not merely discouraged—it is unavailable on the red-state type. section 8.5.1

The pattern is especially useful when a builder must collect required values before construction. Each setter returns the builder in its next state, and `build` exists only on the final state. Missing a required field therefore becomes a compile-time method-availability error rather than a run-time failure. section 8.5.2

A generated type-state builder follows a deliberate sequence:

  • Create a marker trait and empty state structs representing each required field, plus a final state. section 8.5.2
  • Make the builder generic over its state and retain that generic with `PhantomData<T>`. section 8.5.2
  • Return the first state from `builder()`, then have every field method store its input and return the next state. section 8.5.2
  • Implement `build` only for the final builder state. section 8.5.2
Figure 8.6 Desired situation: illegal states are unrepresentable. We generate a struct with a different generic type parameter for every method, only allowing the last one to call build.
figure

The state sequence makes the key constraint visible: only the builder reached through every required setter can call `build`. section 8.5.2

This approach favors an API that makes invalid configurations unrepresentable. It can also guide macro implementation: intermediate states can require all needed information before a final method produces output, while `#[must_use]` can warn when a required returned state is ignored. section 8.11 section 8.6

How Write Powerful Rust Macros covers it

How deep it goes
Works through marker traits, generic state parameters, PhantomData, generated transition methods, and compile-fail testing for a builder.
Examples
Uses a red/green traffic light, a Gleipnir builder with three required fields, and a missing-field compile-fail test.
What it assumes
Assumes Rust generics, traits, implementation blocks, and procedural-macro code while explaining the state encoding incrementally.
What you can do after
Build it from scratch by generating state-specific builders whose final state alone exposes `build`.
How it connects
Connects type state with the builder pattern, newtypes, PhantomData, marker traits, compile-time errors, and the strategy pattern.

Also explained in

Compare how 2 books explain type state →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.