How can a procedular macro know what type is expected from it?

I wanted to create a procedural macro that could be used to fill the content of a const, but for consts we need to explicitly declare the type. How could the macro know what type is expected from it?

e.g. I tried to create a macro that would return a random number. This is how I managed to implement it:

Using:

extern crate random_constant_macro;
use random_constant_macro::get_random;

const RANDOM_CONSTANT: u16 = get_random!(u16);

So I also passed the type as a parameter to the macro. I don't like this repetition.

In the implementation I managed to do this, I could not even figure out how to use generics

extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn get_random(item: TokenStream) -> TokenStream {
    match item.to_string().as_str() {
        "u16" => {
            let x = rand::random::<u16>();
            return format!("{:?}", x).parse().unwrap();
        },
        "i32" => {
            let x = rand::random::<i32>();
            return format!("{:?}", x).parse().unwrap();
        },
        _ => panic!("Unsupported type '{}'", item),
    };
}

I guess I could use a declarative macro to reduce the code-repetition in the macro, but I am quite sure there are better ways to solve this.

BTW I know that fixing a random number at compile time might not be that useful, but it looked like a good and simple example.

It doesn't. It's literally impossible. Macros are expanded before the compiler has fully resolved names, let alone resolved types. Macros also have no access whatsoever to anything other than what you explicitly provide as input.

What you've done (specify the type you want as part of the input) is what you need to do, based on what you've described.

Whatever syntax you were thinking of for the declarative macro you can also do with a procedural macro. If you want to continue down this road, you'll want to look at the syn crate for parsing macro input.

I don't have a problem with it, but I can imagine the reproducible build people looking at this and grinding their teeth. :slight_smile:

3 Likes

For this particular problem I would write a macro

random_init!(const RANDOM_CONSTANT: u16);

which expands to

const RANDOM_CONSTANT: u16 = 9942;

But, you cannot do this for arbitrary types where T: rand:Fill or where rand::Standard: rand::Distribution<T> soundly.

2 Likes

Regarding nondeterministic output, macros are kind of expected to be deterministic in general. I think there may be some caching in incremental builds and rust-analyzer will probably invoke it more than once (or at least cache a different value than what you will see). It's something to keep in mind if it goes beyond experiments.

1 Like

... and I just found the const-random crate doing this.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.