Also, in general, we don't like flags that change language semantics. This one might be acceptable though, but I'm not on the lang/compiler teams.
Static dispatch:
fn show<T>(a: T) -> String
where T: Display
{
Display::fmt(a)
}
fn main() {
show(1_i32);
show(1.0_f32);
}
is (handwaves) first translated to a version with no generics fn show_i32(a: i32) -> String {
i32::fmt(a)
}
fn show_f32(a: f32) -> String {
f32::fmt(a)
}
fn main() {
show_i32(1_i32);
show_f32(1.0_f32);
}
You can probably see how the more permutations of functions you need to generate the slower it gets.Dynamic dispatch:
fn show(a: Box<dyn Display>) -> String {
Display::fmt(a)
}
fn main() {
show(1_i32);
show(1.0_f32);
}
This translates to (handwaves): fn show(a: Box<dyn Display>) -> String {
let fmt_fn = lookup_fn(Display::fmt, typeid_of(a))
fmt_fn(a)
}
fn main() {
show(1_i32);
show(1.0_f32);
}
This is simple to compile, but you need to do some extra work at runtime.I think the real reason is that rust gives additional guarantees about lack of allocation and object layout that are hard to implement with dictionary passing (BitC tried and failed).
However, I think these all operate on boxed objects, which is a little bit easier to do than for Rust, where only some objects are boxed.
In short, I agree.
Haskell's polymorphism is similar, it's true, but I can't speak to their semantics well enough to intelligently comment. They are much less concerned with performance than Rust is, so they tend to do less of the sorts of optimizations that gets Rust into trouble here.