Course contentsShow
← Rust
Lesson 141 of 1,9102. The Ownership ModelFree lesson

Ownership-Based State Machines

Design type-state machines that transition between valid states using ownership movement.

In this lesson, you will learn how to use Rust’s ownership rules to enforce state transitions at compile time, ensuring your program can never be in an invalid state.

The Problem with Traditional States

In many languages, an object has a single type (like Order) with a field like status. You might accidentally call process() on an order that hasn't been paid() yet. The compiler doesn't know your business logic, so it lets this happen, leading to runtime errors.

The Ownership Solution

In Rust, we can represent each state as a distinct type. By consuming the current state and returning a new type, we make it impossible to call methods on the wrong state.

Imagine a Ticket system. You shouldn't be able to redeem a ticket unless it is Purchased.

struct Unpaid;
struct Purchased;

struct Ticket<State> {
    state: State,
}

impl Ticket<Unpaid> {
    fn pay(self) -> Ticket<Purchased> {
        // We move the Unpaid ticket out and return a Purchased one
        Ticket { state: Purchased }
    }
}

impl Ticket<Purchased> {
    fn redeem(self) {
        println!("Ticket redeemed!");
    }
}

Why This Works

Notice that pay takes self by value. This consumes the Unpaid ticket; you cannot use it again. The compiler returns a new Ticket<Purchased>. If you try to call redeem() on an Unpaid ticket, the code simply won't compile because the redeem method is only defined for the Purchased type.

By using the ownership model, we transform business logic constraints into type errors. If the code compiles, the state machine is guaranteed to have followed the correct path. You don't need runtime checks to see if an order is "valid" because the type system itself tracks the progress of the object through its lifecycle.

Key Takeaway: By consuming a struct and returning a new type representing the next state, you force the compiler to act as a gatekeeper for your state machine logic.