10.6 Enums vs. Inheritance in Object-Oriented Programming
OOP programmers might compare Rust enums to class hierarchies. Both model “is-one-of” relationships, but differ in approach.
10.6.1 OOP Approach (Conceptual Example)
OOP uses inheritance and dynamic dispatch (virtual methods):
// Java Example
abstract class Shape { abstract double area(); } // Base class/interface
class Circle extends Shape { /* ... */ @Override double area() { /* ... */ } }
class Rectangle extends Shape { /* ... */ @Override double area() { /* ... */ } }
// Can add Triangle extends Shape later without changing Shape/Circle/Rectangle.
// Usage:
// Shape myShape = new Circle(5.0);
// double area = myShape.area(); // Dynamic dispatch calls Circle.area()
- Extensibility: Open. New subclasses can be added easily.
- Polymorphism: Uses dynamic dispatch at runtime.
10.6.2 Rust’s Enum Approach
Rust enums define a closed set of variants, using static dispatch via match
:
enum Shape { Circle { radius: f64 }, Rectangle { width: f64, height: f64 }, // Adding Triangle requires modifying this enum definition // and all 'match' expressions handling Shape. } impl Shape { fn area(&self) -> f64 { // Static dispatch: compiler knows which code to run based on variant match self { Shape::Circle { radius } => std::f64::consts::PI * radius * radius, Shape::Rectangle { width, height } => width * height, // If Triangle were added, compiler ERRORs until handled here. } } } fn main() { let my_shape = Shape::Circle { radius: 5.0 }; let area = my_shape.area(); // Calls method, uses match internally println!("Enum Circle Area: {}", area); }
- Fixed Set: Closed. Adding variants requires modifying the enum and related
match
es (compiler enforces this). - Static Dispatch:
match
determines behavior at compile time. No runtime dispatch overhead. - Data & Behavior: Enum lists forms;
impl
centralizes behavior.
10.6.3 When to Use Enums vs. Trait Objects
-
Use Enums When:
- The set of variants is fixed and known upfront.
- You want compile-time exhaustiveness checks.
- Static dispatch performance is preferred.
- Modeling variants of a single conceptual type.
-
Use Trait Objects (
dyn Trait
) When:- You need extensibility (adding new types implementing a trait later).
- You need a heterogeneous collection of different types sharing a trait.
- Dynamic dispatch is acceptable/required.
Trait objects (Chapter 20) offer dynamic polymorphism closer to the OOP style.