4 ms·
If monomorphization is the killer, has the rust team considered adding a flag to compile all generics as trait objects in debug mode? Maybe there are some reaso
by nitsky 5y ago
If monomorphization is the killer, has the rust team considered adding a flag to compile all generics as trait objects in debug mode? Maybe there are some reasons this is impossible I haven’t thought of.
- steveklabnik 5y agoPeople have asked, but only some traits are object safe, for example. Maybe someone could come up with a way to make it work, but nobody has attempted to put in that work, and it would be much more complex than "oh just make this flag run the "trait object" codegen instead of the "monomorphization" codegen." 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.
- anchpop 5y agoWhy would it change language semantics? Haskell’s polymorphism to my eye seems about the same as rust’s, and it doesn’t need to worry about object safety
- steveklabnik 5y agoI mean, object safety is one exact example. Every trait can be monomorphized, but not all traits can be made into an object. So "turn every generic into an object" just doesn't work. When you request a generic bound, you're saying that you want certain performance characteristics. 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.
- iudqnolq 5y agoHaskell's polymorphism isn't the same as rust. Rust has two kinds of polymorphism. 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.
- gpderetta 5y agoThat's not an answer though. I believe (but I know little of both Haskell and Rust) that Haskell polymorphism is at least as powerful as rust static polymorphism but it is normally implemented via type erasure. 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).
- Tyr42 5y agoI agree somewhat, in Haskell you can write generic functions without traits, and I think they are not monomorphized. (map for example.) 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.