Open nvzqz opened 5 years ago
As @memoryruins pointed out to me, apparently typeof
is already a reserved keyword as seen in issue-42060.rs
and issue-29184.rs
. However, I think the above proposal is a more elegant approach than C-style typeof()
.
Interesting idea!
One obstacle might be parsing. Similarly to const generics, you have an expression appearing in type position without advance notice to the parser. With const generics, the current solution is to require {}
around all expressions other than literals and identifiers... but transposing the same rule here, you‘d have to write things like {foo()}.type
, which I think looks more awkward than just using typeof. On the other hand, the const generics rule is conservative, and it might be possible to relax it in the future to the point that you wouldn’t usually need braces; if so, the same could apply here.
You bring up a very good point. I suppose {expr}.type
would be fine in the meantime but that is quite awkward. Do you know what work is being done right now to eliminate that from const
generics?
For const generics, I think everyone is waiting for the stuff that's already accepted to be implemented before proposing any extensions. (It's been a long wait, but thanks to varkor's work it seems pretty close to being ready!)
I'm also only speaking for myself when I say it might be possible to relax the rule. I think I've seen some talk of it in the past, but I don't remember where/when exactly, and I have no idea what anyone thinks of the idea now. shrug
In the macro example I gave, would that work without needing {$x}.type
since the parser knows that the tokens are being treated as an expression?
Regarding prior art, note
In C, there's
typeof()
, which this feature would be an exact parallel to.
That one is a GCC extension. However, C++ introduced it under the name decltype
(which is more explicit in that it returns the declared and not the dynamic type—so it might be actually better name here too).
It was introduced because many template expressions in C++ return values where the type is either insanely complex, or the specification does not give it any name.
Rust uses generics even more than C++, so it also has all the same problems with the return values, and while the existential types cover many of the use-cases, it does not cover all of them.
It was introduced because many template expressions in C++ return values where the type is either insanely complex, or the specification does not give it any name.
The latter is true for Rust as well, now that async fn
is here (or lambdas). And however the async fn
is nice I stumble into the issue here every time I try to do something more interesting with futures (like storing them into a structure) - I need to somehow name the type and the only way to do it currently is to box it. The only alternative is to write them manually without any lambdas and get 'insanely complex' type.
This is also useful for functional programming when the compiler can't figure out how a closure will be called and requests that the type of its arguments be manually specified. If these arguments are themselves closures, as is often the case with curried higher-order functions, then doing so currently requires the overhead of using dyn
, even if it is known at compile-time (and even write-time) the exact closure that will be passed in.
From my experience, this (along with the lack of HKTs, which can be worked around with https://github.com/rust-lang/rust/issues/44265) makes proper functional programming quite a hassle in Rust.
There is also std::result_of
and std::invoke_result
in C++ that could be handy to store results of functions with impl return types to structs.
Something like:
async fn make_foo() -> Value {
...
}
struct Bar {
foo_future: result_of!(make_foo);
}
async {
let foo_future = make_foo();
let bar = Bar { foo_future };
}
Any updates on this?
I ran into the same problem when working with libafl
that relies heavily on generics. So the exact types can get pretty long. Example of a type that needs to be explicitly passed:
CommandExecutor<
(StdMapObserver<u8, false>, (SanitizerHashShmemObserver, ())),
StdState<BytesInput, InMemoryCorpus<BytesInput>, RomuDuoJrRand, OnDiskCorpus<BytesInput>>,
AdapterExecutor,
>
which could imaginary be replaced with executor.type
.
do we still need this with TAIT now?
Yes, traits lose info compared to a specific type.
It would be very useful in certain contexts (such as macros) to be able to get the concrete type of some variable through a given binding.
The idea for the syntax came from the upcoming
.await
syntax. Since this sets the precedent of having reserved keywords in a property position, I think that this would fit well. However, I understand that—unlike?
and.await
—.type
acts more as an accessor instead of a control flow construct. Depending on one's point of view, this may be a good thing since it aligns somewhat with how property accessors appear today.Examples
It would work as follows:
How it may be used within a macro:
Currently, the only way of getting the "type" of a binding within the context of a macro doesn't allow for the above code to work. For example:
Prior art
In C, there's
typeof()
, which this feature would be an exact parallel to.In Swift, there's
type(of:)
, which returns a runtime value with metadata for the type of a value. This is different in that this proposedx.type
would only be usable within a compile-time type context, not a runtime one.For those who want to go spelunking into other languages, there's a
typeof
Wikipedia article.Why I want this
It would make the implementation of
assert_eq_size_val
a bit less awkward.I can also get more imaginative about use cases outside of the given examples upon request.