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

The Duality of Trait Bounds and Pattern Matching

Decide when to use trait bounds for logic and when to use internal enum matching.

In this lesson, you will learn how to choose between the open-ended flexibility of trait bounds and the closed-set precision of pattern matching to handle multi-type logic.

The Architectural Choice

When you need a function to handle different types, you face a fork in the road. Do you use trait bounds (static polymorphism) or pattern matching (enums)?

Trait Bounds: The Open Path

Trait bounds are for when you want to write logic that works for any type, as long as it behaves in a certain way. This is "open" because a user of your library can define their own type, implement your trait, and your code will accept it without modification.

fn process_item<T: Display>(item: T) {
    println!("Item: {}", item);
}

Use this when the set of types is unknown or user-extendable.

Pattern Matching: The Closed Path

Pattern matching is for when you know the complete list of possibilities upfront. By using an enum, you create a "closed" system. Because the compiler knows every possible variant, it can guarantee your logic is exhaustive.

enum Shape {
    Circle(f64),
    Square(f64),
}

fn area(shape: Shape) -> f64 {
    match shape {
        Shape::Circle(r) => 3.14 * r * r,
        Shape::Square(s) => s * s,
    }
}

Use this when the behavior is specific to a finite set of known types, and you want the compiler to enforce that every case is handled.

How to Decide?

Think about the Expression Problem:

  • Add new types easily? Use Trait Bounds. You can add a new type and implement the trait without touching existing functions.
  • Add new operations easily? Use Enums and Pattern Matching. You can add a new function that matches on the enum variants without having to modify the enum definition or add methods to existing types.

If you find yourself using match statements inside a trait method to handle a fixed set of types, you are likely mixing these patterns. Simplify by either collapsing those types into an enum (if the set is closed) or moving the logic into the trait implementations themselves (if the set is open).

Key Takeaway: Use trait bounds when you want to support an infinite, open set of types; use pattern matching when you have a finite, closed set of variants that you need to handle exhaustively.