There's this widespread notion that using dynamic dispatch should reduce binary size (example). I've recently decided to make use of this optimization in our codebase. I came up with a clean wrapper that should make using dynamically dispatched collections easy, but the file sizes get larger, not smaller. I've tried it with smaller projects as well but they all grow in size after implementing dynamic dispatch. Is this whole "dynamic dispatch reduces file size" thing a myth, or am I doing something wrong?
I've made this small repo to demonstrate the issue. It has 3 modules that do the same thing, but one uses static dispatch, another uses dynamic dispatch explicitly, and the third uses my dynamic dispatch wrapper. Somehow, the generic version is much smaller than the others. file-bloat/lib.rs at main · MendyBerger/file-bloat · GitHub
Comparing Vec<u64> and Vec<Box<dyn Any>> is far from a fair fight, because you have significantly more allocations to do in the latter case. A closer comparison would be between Vec<Box<u64>> and Vec<Box<dyn Any>>, as those have the same basic shape.
However, you're not actually utilizing any dynamic dispatch here. The only reduction in monomorphization is that you're calling Vec::<Box<Any>>::push instead of Vec::<u32>::push, Vec::<u64>::push, and Vec::<char>::push, and Vec::<f64>::push. And most of Vec's allocation code is already manually polymorphic (independent of the generic type), so the benefit of avoiding monomorphization here is extremely minimal. Then if you're using DynWrapper, you're completely eliminating any benefit of polymorphization, because you're not even polymorphizing Vec, in addition to not doing any sort of dynamic dispatch on T. DynWrapper<T> is essentially no different to using Box<T> (except that it's slightly bigger due to carrying around a dyn vtable pointer).
When people say that dynamic dispatch reduces binary size, they're referring to the difference of e.g.
But the compiler needs to prove that's true. It's not immediate by the fact that it's not actually stored in the fields since any code that uses a DynWrapper<T> could potentially depend on that T. Note that this could happen very subtly, for example some code very down the line could call std::any::type_name and get the name of the type you passed in, which includes the concrete type of TRust Playground