Course contentsShow
← Rust
Lesson 769 of 1,9106. Generics and Static DispatchFree lesson

Transactional Systems with Generics

Build generic commit/rollback state machines that track resource integrity.

Building Robust State Machines for Transactions

In this lesson, you will learn how to use Rust’s type system to create "state-aware" transaction wrappers that ensure resources can only be committed or rolled back when they are in the correct phase.

The Problem: Accidental Misuse

In complex systems, it is easy to accidentally call commit() twice or try to rollback() a transaction that has already been finalized. Instead of relying on runtime checks (which are prone to human error), we can encode these legal transitions directly into the type system using Generics and PhantomData.

Encoding States with Generics

We define a Transaction<S> where S represents the current phase. By creating empty structs to act as state markers, we can ensure that methods are only available when the transaction is in the appropriate state.

struct Pending;
struct Committed;
struct RolledBack;

struct Transaction<State, R> {
    resource: R,
    _state: std::marker::PhantomData<State>,
}

impl<R> Transaction<Pending, R> {
    pub fn commit(self) -> Transaction<Committed, R> {
        // Logic to finalize resource
        Transaction { resource: self.resource, _state: std::marker::PhantomData }
    }

    pub fn rollback(self) -> Transaction<RolledBack, R> {
        // Logic to revert resource
        Transaction { resource: self.resource, _state: std::marker::PhantomData }
    }
}

Why This Works

Notice that commit and rollback are only defined for Transaction<Pending, R>. If you hold a Transaction<Committed, R>, you literally do not have the methods commit or rollback available in your scope. This provides static dispatch—the compiler checks the validity of your transaction flow before your code ever runs.

By parameterizing your resource R alongside the state S, you create a system that is both generic (works with any resource type) and strictly guarded against invalid state transitions. If a developer attempts to call rollback on a Committed transaction, the compiler will stop the build, effectively turning potential runtime bugs into compile-time errors.

Key Takeaway: Use generic state markers to consume the transaction and return a new state, effectively using the type system to enforce strict workflow integrity.