Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get type of an arbitrary expression #2704

Open
nvzqz opened this issue May 29, 2019 · 12 comments
Open

Get type of an arbitrary expression #2704

nvzqz opened this issue May 29, 2019 · 12 comments
Labels
A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@nvzqz
Copy link
Contributor

nvzqz commented May 29, 2019

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:

let x = 20;

// Equivalent to `typeof(x) y = x;` in C
let y: x.type = x;

type X = x.type;

static_assertions::assert_eq_type!(x.type, i32);

How it may be used within a macro:

macro_rules! do_stuff {
    ($x:expr) => {
        let y = <$x.type>::new(/* ... */);
        $x.do_stuff_with(y);
    }
}

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:

macro_rules! do_stuff {
    ($x:expr) => {
        fn with_type_of<T>(x: T) {
            let y = T::new(/* ... */);
            x.do_stuff_with(y);
        }
        with_type_of($x);
    }
}

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 proposed x.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.

@nvzqz
Copy link
Contributor Author

nvzqz commented May 29, 2019

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().

@comex
Copy link

comex commented May 29, 2019

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.

@nvzqz
Copy link
Contributor Author

nvzqz commented May 29, 2019

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?

@comex
Copy link

comex commented May 29, 2019

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

@nvzqz
Copy link
Contributor Author

nvzqz commented May 29, 2019

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?

@jan-hudec
Copy link

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.

@jonas-schievink jonas-schievink added A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC. labels Nov 19, 2019
@uzytkownik
Copy link

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.

@nvzqz nvzqz changed the title .type for getting concrete type of a binding Get type of an arbitrary expression Oct 17, 2021
@MithicSpirit
Copy link

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 rust-lang/rust#44265) makes proper functional programming quite a hassle in Rust.

@akhilman
Copy link

akhilman commented Oct 30, 2023

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 };
}

@momvart
Copy link

momvart commented Jun 4, 2024

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.

@SOF3
Copy link

SOF3 commented Jun 5, 2024

do we still need this with TAIT now?

@Lokathor
Copy link
Contributor

Lokathor commented Jun 5, 2024

Yes, traits lose info compared to a specific type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-typesystem Type system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

10 participants