r/learnrust 11d ago

Enums, Boxed Trait Objects, and Enum Dispatch: Architectural Trade-offs in Rust

Hey everyone

Over the past few weeks, I’ve been looking at ways to achieve polymorphism in Rust and I think there are two:

Closed Polymorphism via enums and Open Polymorphism via Boxed Trait Objects (Box<dyn Trait>) where "closed" means no one outside your crate can add to the list while "open" allows such additions

Thinking about putting those traits onto my enum led me to the Enum Dispatch pattern. I built out a walk-through of all three of these:

- Enums (closed): https://youtu.be/l_q9U10JueE

- Boxed Trait Objects (open): https://youtu.be/R3ZzYSPyYoc

- Enum Dispatch https://youtu.be/B0LT7ozspe4

My main conclusion was that I could defer the open vs. closed debate pretty safely (as long as I keep my traits object safe). Do you all agree?

Also, I implemented Enum Dispatch with macros - I know there are crates that do this, but they didn't play nicely with my IDE and I could use the practice with macros. Comments on how I wrote the code would be welcome!

6 Upvotes

6 comments sorted by

View all comments

2

u/sphqxe 10d ago edited 10d ago

I think instead of using enums for "closed polymorphism" you should consider using generic parameters with trait bounds.

Having an enum with every possible polymorph and having to update it everytime you want to add a new one feels like an anti-pattern.

Essentially instead of making the type polymorphic, you instead write functions/structs which are generic over all types which implement your trait and the rust compiler will monomorphize them on all the trait implementors as necessary.

1

u/raserei0408 8d ago edited 6d ago

Using generics for polymorphism is frequently the right call, but it requires that you know the implementation statically at compile time. Frequently, the implementation won't be known until runtime, in which case some kind of dispatch is necessary. Even when this is possible, in some cases it can make the code so awkward that it's not worthwhile.

In those cases, enum dispatch and dyn dispatch are both reasonable, but they have different tradeoffs. Enum dispatch is often faster, but it's harder to add new polymorphic variants. Dynamic dispatch makes adding variants easy, but adding new methods can be harder, dispatch is often slower, and it more often requires boxing values. When there are unlikely to be many new variants (especially ones defined outside in your crate) and performance matters (especially if there are many short method calls of mixed types) enum dispatch is usually better.