RustArena

Step 6 of 7

Enums or trait objects?

You've now seen two ways to handle "one of several kinds of thing": an enum with a match (enums module), and a trait with dyn. Here are the same shapes as an enum:

enum Shape {
    Circle { radius: f64 },
    Rect { w: f64, h: f64 },
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { radius } => 3.0 * radius * radius,
            Shape::Rect { w, h } => w * h,
        }
    }

    fn name(&self) -> &'static str {
        match self {
            Shape::Circle { .. } => "circle",
            Shape::Rect { .. } => "rect",
        }
    }
}

fn main() {
    let shapes = vec![Shape::Circle { radius: 1.0 }, Shape::Rect { w: 2.0, h: 3.0 }];
    for s in &shapes {
        println!("{} {}", s.name(), s.area());
    }
}
circle 3
rect 6

No Box, no trait, and Vec<Shape> holds both kinds. So which should you use? It depends on what will change:

Enum Trait objects
The set of kinds closed: all variants are listed in one place open: anyone can add a type, even in another crate
Add a new kind add a variant, then fix every match (the compiler lists them) write one new impl; nothing else changes
Add a new operation write one new method with a match add a method to the trait, then implement it for every type
Memory stored inline, no allocation one Box per value (heap)
Extras derive(Debug, Clone, PartialEq), pattern matching on the data needs a dyn-compatible trait; can't easily inspect the concrete type

(Computer scientists call this trade-off the expression problem: one design makes new kinds easy, the other makes new operations easy.)

Rules of thumb

Common mistakes

Your task

A checkout used to model payment methods as three structs behind a Payment trait. The set of methods is fixed and owned by the team, so it's being rewritten as an enum. Implement label and fee_cents with one match each:

Challenge 2

A chat app's Message enum needs a new kind: Video { url: String, seconds: u32 }. Add the variant, then let the compiler's non-exhaustive patterns errors guide you to every match to update:

Challenge 3

The other side: an open set. A web framework (pretend you can't edit it) checks every request path against a list of Box<dyn Filter>. Without changing Filter or check, add two filters of your own:

Previous: Dyn compatibility (object safety) Next: Downcasting with Any (briefly)