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

The Strategy Pattern for Algorithmic Flexibility

Swap algorithms at runtime while guaranteeing consistency via trait bounds.

The Strategy Pattern for Algorithmic Flexibility

In this lesson, you will learn how to decouple an object’s behavior from its implementation by swapping algorithms at runtime using Rust traits.

Why use the Strategy Pattern?

Often, you might have a component that needs to perform a task—like sorting a list or calculating a tax rate—but the best way to do that task depends on the current context or configuration. Instead of writing a massive match statement or if-else block inside your main logic, you can encapsulate each "strategy" into its own type that satisfies a common trait.

Implementing Flexibility with Traits

To implement this, define a trait that outlines the method signature for your algorithm. Any struct that implements this trait becomes a "strategy." Your main component then holds a reference to an object that satisfies that trait.

trait SortingStrategy {
    fn sort(&self, data: &mut [i32]);
}

struct QuickSort;
impl SortingStrategy for QuickSort {
    fn sort(&self, data: &mut [i32]) { /* Efficient quicksort logic */ }
}

struct BubbleSort;
impl SortingStrategy for BubbleSort {
    fn sort(&self, data: &mut [i32]) { /* Simple bubble sort logic */ }
}

struct Sorter {
    strategy: Box<dyn SortingStrategy>,
}

impl Sorter {
    fn execute(&self, data: &mut [i32]) {
        self.strategy.sort(data);
    }
}

By using Box<dyn SortingStrategy>, the Sorter doesn't need to know which algorithm it is running. It only knows that the object in its strategy field provides a sort method. This allows you to inject different algorithms at runtime (e.g., using QuickSort for large datasets and BubbleSort for tiny ones) without modifying the Sorter code itself.

Ensuring Consistency

Because we use a trait bound, the Rust compiler guarantees that any strategy you inject will adhere to the exact interface expected by your system. This removes the risk of runtime errors where a component expects a specific input but receives something else, keeping your architecture robust while remaining highly adaptable.

Key Takeaway: Use the Strategy Pattern to encapsulate related algorithms behind a common trait, allowing you to swap behaviors dynamically without coupling your main logic to specific implementations.