I think static vs dynamic dispatch trade-off decision is pretty similar to inlining:
Static dispatch and inlining can have performance benefit.
Static dispatch and inlining can increase binary size.
So for dispatching, we’re making a trade off between 1 and 2, at the time we write code, but we won’t have a clear trade-off picture until we have a complete code and compile (or even better, runtime? PGO?), just like inlining.
Therefore I think ideally static vs dynamic dispatch decision should be at compile time, with an optional user control flag per function.
I think you’re saying these should be an optimization like inlining, not something the user knows or cares about. This isn’t possible, because trait objects and type parameters have significantly different semantics from one another. As examples - trait objects traits have to be object safe, and type parameters cannot be used to construct heterogenous collections.
It is theoretically possible to dispatch trait object method calls statically in some cases (devirtualization), and to dispatch generic functions dynamically in I believe all cases (Swift does this AFAIK). Not completely sure of applicability to Rust.
But its not true that Rust's generics could be converted to Rust's trait objects in all cases - this is exactly what object safety is about. With a different language model (e.g. one which allows boxing / arcing things implicitly) it becomes much easier of course.
Not trait objects. More like Haskell’s dictionary passing for type classes, except also with size/alignment information passed around at runtime. You’re right that boxed vs. unboxed presents a challenge, which I’d forgotten, though I suspect even that could be done with alloca-like stuff…