Ramblings of an aging IT geek
← Ramblings of an aging IT geek
rust

Trait Objects vs Generics, With Numbers

A quick Criterion benchmark of dynamic dispatch versus static dispatch in Rust, and why the answer was less dramatic than the arguments about it.

A screen full of code

Every Rust thread eventually argues about Box<dyn Trait> versus generics, usually in tones suggesting one of them will set your house on fire. I got tired of guessing, so I benchmarked it.

The setup was deliberately boring: a trait with one method that does a tiny bit of arithmetic, called in a tight loop ten million times. One version uses a generic bound and monomorphises. The other goes through a &dyn reference and a vtable lookup. Criterion, release build, several runs.

The static dispatch version came in around 4ms. The dynamic one around 11ms. So yes, dynamic dispatch was measurably slower, by roughly a factor of two and a bit, because the vtable indirection blocks inlining and the loop body is otherwise almost nothing.

But look at the absolute numbers. That gap is a few nanoseconds per call. If your method does anything real, a hash, an allocation, a syscall, the dispatch cost vanishes into the noise. I reran it with a HashMap lookup inside the method and the two were within margin of error.

So the honest answer is the unsatisfying one. Reach for generics by default because they are zero-cost and the compiler likes them. Reach for dyn when you genuinely need a heterogeneous collection or want to keep compile times and binary size down. Do not reach for either because a forum told you the other one is slow. Measure the thing you actually built.