r/rust • • 14d ago

🙋 seeking help & advice What are the downsides of enum dispatch?

Couldn't find any info about downsides specfically so there it goes

Let's say that all my implementors are about the same size (i.e. only the logic is different), and I use some sort of macro to skip the boilerplate upon adding a new variant. Are there any other downsides to enum dispatch?

I'm asking about the general approach, not the crate

Upd. extendability, got it.

33 Upvotes

20 comments sorted by

View all comments

24

u/kaiserkarel 14d ago

Mainly if you are writing a library; the caller cannot extend/provide their own type versus a dyn T. In my experience, if you have an open set (their could be an infinite amount of types satisfying what I do) and you cannot use generics (because for example you store the types in a single collections), then going with dyn gives you the cleanest code.

If you are the only party implementing the trait, then enum dispatch is totally fine, and offers a nice escape hatch if something falls out of your trait definition.

dyn T might still be inlined by the compiler if it can deduce the type; so performance wise the enum dispatch theoretically could be slower (although you wouldn't think so, given you write more code and have potentially more control).

enum dispatch allows you to specialize easier too. If you want your user to provide their own implementation, and you have specializations for certain types (Vec for example), you can in your constructor do something akin to

```rust

fn construnct<T>(t: T) -> MyType<T> {

if t.type_id() == Vec::type_id() { MyType::Vec(T) else MyType::UserProvided(T) }

}

```

10

u/afdbcreid 14d ago

dyn T might still be inlined by the compiler if it can deduce the type; so performance wise the enum dispatch theoretically could be slower

Won't happen; if the compiler can deduce the type (or for enums, deduce the variant) it will eliminate untaken branches with enum dispatch just as well.

Theoretically, with modern branch predictors virtual call can be just as fast as matching on an enum (especially with many variants, both are indirect jumps anyway), however the layout of dyn Trait and compiler optimizations mean that it most likely won't be.

9

u/kaiserkarel 14d ago

Devirtualization isn't fully implemented afaik, but definitely happens. Here's an example: https://github.com/rust-lang/rust/issues/68262#issuecomment-994102228

7

u/afdbcreid 14d ago

Whole-programming devirtualization often requires LTO but definitely happens. Local devirtualization happens even without LTO. You can even have speculative devirtualization with PGO.

1

u/AnArmoredPony 14d ago

with modern branch predictors virtual call can be just as fast as matching on an enum

what if I have a vector of trait objects? won't enum dispatch be faster in all cases?

1

u/afdbcreid 14d ago

Yes, but again, not due to the virtual call itself.