Course contentsShow
← Rust
Lesson 610 of 1,9105. Type System and TraitsFree lesson

The Command Pattern for Transaction Systems

Model undoable or recordable actions using a common trait interface.

In this lesson, you will learn how to encapsulate actions as objects to enable undoable transactions using the Command Pattern.

The Command Pattern Concept

In a transaction system, you often want to keep track of a sequence of operations so that you can reverse them if a failure occurs. Instead of executing code directly, the Command Pattern packages an operation—along with its necessary data—into a structure that implements a common trait.

By defining an interface like Command, you decouple the request for an action from the execution of that action. This allows you to push these objects into a Vec (a history stack) and pop them off one by one to trigger an undo() method.

Implementation with Traits

To build this, we define a trait that forces every operation to define its logic for both doing and undoing the work.

trait Command {
    fn execute(&mut self);
    fn undo(&mut self);
}

struct AddToBalance {
    amount: i32,
    account: *mut i32, // Simplified for illustration
}

impl Command for AddToBalance {
    fn execute(&mut self) {
        unsafe { *self.account += self.amount; }
    }
    fn undo(&mut self) {
        unsafe { *self.account -= self.amount; }
    }
}

Managing Transactions

Because all commands implement the same trait, you can store a heterogeneous list of different actions in a single collection. Using composition via trait objects, your transaction manager doesn't need to know if it is dealing with a balance adjustment, a user rename, or a database record change; it only needs to know how to call .execute() or .undo() on the items in its stack.

When an error occurs during a batch of operations, you simply iterate backward through your history stack and invoke .undo() on each command. This turns complex, multi-step state changes into atomic, reversible transactions without polluting your business logic with manual rollback code.

Key Takeaway: By encapsulating actions within a trait-based Command interface, you can treat operations as data, enabling robust undo-redo functionality and reliable transaction rollback in complex state systems.