@willi_kappler: D's typeof
is analogous to C++'s decltype
, which has been used in many places that we've traditionally identified the need for impl Trait
in Rust. For example, a recognized pain point with writing abtract Rust is when we have generic-heavy code, like chained Iterator impls, or Futures in the async tokio
crate, that commonly result in monomorphized types that are tough to write by hand. Try to write the return type of expression like this:
vec![1, 2, 3].iter().map(|x| x + 2).filter(|x| x % 2 == 0).map(|x| x * 2)
The return type here is actually std::iter::Map<std::iter::Filter<std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@...]>, [closure@...]>, [closure@...]>
. Gross, right? But we deal with this in Rust because we favor existential types and thus monomorphization, so we can optimize more through the functional paradigm we use regularly in Rust. This way, rustc
can make some very expressive code zero-cost.
For D, it's much the same. In a similar situation, we don't necessarily want to use dynamic dispatch (interface or abstract class instance in D, trait object in Rust), but writing the type of a particular expression might be difficult or even impossible. In D's case, an additional consideration is that there's something called "Voldemort" types, where a function's return type isn't necessarily declared to client code. An example from D's docs:
// Note: the return type is auto, because we cannot actually name it outside the function!
auto createVoldemortType(int value)
{
struct TheUnnameable
{
int getValue() { return value; }
}
return TheUnnameable();
}
auto voldemort = createVoldemortType(123);
writeln(voldemort.getValue()); // prints 123
auto
in this case, is totally necessary in order to get a result from the return of createVoldemortType
. If we want to use a type variable for that return anywhere else, like in a template function, we would need typeof
. That's because while the type of a Voldemort type is unavailable to client code, the compiler still allows us to USE the type when exposed via return value, though it MUST remain unnamed (hence, "Voldemort" types!). Type inference in Rust is equivalent to using auto
, but that doesn't cover cases where a library would need to explicitly define types, like in a signature for a function.
Rust has a rather different design for type usage -- if client code can use a given type in a library, then it needs to be pub
, and have it's declaration out in the open. For this reason, many libraries use their own wrapper structs, instead of giving back complicated Iterator types. This may become entirely unnecessary in the future once impl Trait
lands, which I'm super excited for!
As a user of D and Rust, I'd also like to clarify that D makes heavy use of compile-time templating facilities, and typeof
is one of those -- in none of the examples that @leonardo showed were runtime facilities being used. In fact, D's compile-time facilities are pretty expansive, and it's not uncommon to work with entirely abstract compile-time-generated types in libraries. The Unqual template that @leonardo used above is a good example.
Note for @leonardo: AFAIK, type inference has not ever been a bottleneck for the Rust compiler. I don't know if that was the intent of your comment with type inference and compilation speed, but I'd be interested to know just how much of an impact that particular feature has.
EDIT: Responding to @dandyvica's original question, the bottom line for Rust is:
-
stringof
: Coming someday!
-
static if
: Uncertain. We'll make progress with const generic args, but nothing as powerful has been planned AFAIK.
-
typeof
: We'll get something to handle common use cases for return type inference with impl Trait
for return types, but for dealing with abstract types nothing seems planned for this AFAIK. That means for things like the length
example @leonardo gave, there's no equivalent planned. They sound interesting, though, and I'm sure an RFC could be made!