by JoshTriplett | 2025-12-17 22:34:30+00:00
In (2), I'm surprised to hear your description of Rust's generics as "ad-hoc polymorphism". I tend to hear that term used to describe non-trait based systems, like C++ generics (in the absence of "concepts"), which act like a compile-time version of duck typing. I think of Rust's generics as trait-based polymorphism, not ad-hoc polymorphism, and I prefer the former over the latter. To a first approximation, I see Rust's generics system as somewhat related to Haskell's. I would be interested to better understand how you see the differences.

From the way you're describing it, it sounds like you might not fundamentally object to generics in general, just to the idea of doing it through monomorphization rather than "dyn Trait"-style vtables? If so, then that's understandable. I like that Rust has both options, but I can absolutely appreciate preferring to do everything via vtables, which has both advantages (code size, one kind of conceptual simplicity) and disadvantages (more difficult to do per-implementation optimization, without doing devirtualization which amounts to monomorphization anyway).

We do have some forms of first-class "type functions" or "type constructors" or "rank-2 polymorphism", in the style of Haskell, so that you can be generic over something like `Vec`. It's a little indirect at the moment, going through traits with associated types, and I'd love to see a more direct version where you can literally write `Something<HashMap>` or `Something<BTreeMap>` and have those be type functions of one parameter (Haskell "kind" star -> star).

In any case, we have talked many many times about the idea of having more global options to say "don't monomorphize by default, do everything with trait objects". We also are likely to build a stable ABI atop trait objects and vtables eventually, so that it's possible to pass around pointers to data structures and their associated functions, without relying on the layout of the structure.

For (1), I do think we need some mechanism to integrate dependencies. I can appreciate the aversion to the default of "get things from the network". We do try to have good support for vendoring and similar, and this is critical for many projects (e.g. Rust in the Linux kernel, or Rust in CPython, are not going to allow downloading dependencies from the network). All that said, there is a tradeoff between "one first-class package manager and build system" and "lackluster support for various build systems", and Rust picked the former deliberately. We do like being able to implement things in rustc and plumb them through Cargo, so that they're available through the whole stack when that's necessary to make a feature useful. But we also have many users who need to use rustc without cargo, and who ensure that rustc features work with other build systems, notably large corporate monorepo build systems.

As for (3), fair enough, that is very much a philosophical difference between you and Rust, and Rust isn't likely to change that. I do think we're going to make ever more sophisticated ownership semantics in the future (e.g. making self-referential data structures possible), but I doubt Rust's model will stop fundamentally being based around ownership and borrowing.