While the templates are technically untyped (in the sense that there are no type-classes or traits in D), you can use template constraints to achieve something similar: https://dlang.org/concepts.html. Essentially you'd write a compile time isMonad predicate that only returned true if certain functions were defined for a type.
Even without that, the key thing is it's still possible to implement monads in the C++ style (https://stackoverflow.com/a/2565191/2553416), whereas it's impossible to write code to do the same thing in current Rust. It just gives much worse compiler errors if you use it wrong compared to if the language had built in type-classes / traits.
Template constraints aren't the same thing as typed generics. The key issue is that typed generics influence the typechecker. For example, you can write "let x = Default::default(); f(x);" and the compiler can determine the type of x from the type of f.
Also, there is the "higher" crate for higher-kinded types if you need them, so it is possible to write a monad typeclass in Rust. It's not exactly something you'd want to use, though.
>For example, you can write "let x = Default::default(); f(x);" and the compiler can determine the type of x from the type of f.
That would be less helpful in C++/D because their compilers don't do Hindley-Milner style bidirectional inference, so even if they had typed generics they wouldn't necessarily be able to do that inference. But I suppose that's an argument in favour of Rust, stronger type inference.
>It's not exactly something you'd want to use, though.
Ergonomic HKT can make writing various kinds of utilities easier. E.g. imagine I want to write a function Singleton that takes a collection C (vector, list, stream...) and an item of type T, and returns C<T>, a collection containing a single instance of that type. I can write that in Haskell, D or C++, but not in Rust. Or perhaps more usefully, a function Fill, that returns a container C containing n items of type T. For Rust it seems I'd need to write a separate implementation of Fill for each container type, I couldn't write one parameterised over any C?
Overloading does not conflict with type inference: it just means that type annotations are sometimes necessary to get down to concrete types for execution. From "f(x)" we can infer that the type of x is one of f's overloaded argument types.
Because the templates aren't typed at all. I'm not sure you can call untyped templates "Haskell-style".