| This wouldn't work for D. D doesn't constrain return types to something less than what they are. A Range is not just an Iterator, it has optional pieces that depend completely on the given type. For example, the return of map could provide indexing, or it could provide forward and backward iteration, or it might have methods that are completely unrelated to the type. There is no good reasonable and non-confusing way to describe all the things map could return depending on the input. It's much better to just describe it conceptually in the human-readable docs, and let the person understand the result. I'll note that just above the function map in D's source is the documentation. You just need a little more context, and it will describe what map returns in a much more (IMO) useful fashion than a return type that might be several lines long and consist of various static conditionals: "The call map!(fun)(range) returns a range of which elements are obtained by applying fun(a) left to right for all elements a in range." This is the difference between duck typing and generics. |
You can provide more interfaces in Rust:
but you can't provide "conditional" interfaces (for most interfaces at least), e.g., this won't work: where `?Index` reads as "maybe implements Index".To allow that you would essentially need to say that "if the input implements `Index`, the output implements `Index`":
The type system implementation already supports these types of constraints, but there isn't a language extension that exposes that. I don't see any fundamental reasons that make this impossible, but there are many trade-offs involved.Notice that, for example, the output Range does not implement the same interfaces as the input range, e.g., the input Range implements an `Index` interface over a range of `T`s, but the output Range implements an `Index` interface over a range of `U`s. In D this is super implicit in the implementation details (body) of an equivalent `map` function, but in Rust it needs to be part of the type signature to avoid changes to a function body to silently cause API breaking changes. In D, you could change the body of map to map only from Range(T) -> Range(T), without changing its interface, and that would break all code using it to map a Range(T)->Range(U).