Macros and generics


#1

Is there a way to use generic parameters in macros (either macro_rules macro or plugin type of macros) ?

Currently when I’m trying to do something like that it fails at compile time (error below code)

Thanks :slight_smile:

struct HelloWorld;
impl HelloWorld {
    fn hello_world() {
        println!("Hello world !!!");
    }
}

macro_rules! say_hello {
    ($generic:ty) => {
        $generic::hello_world();
    }
}

fn say_hello<T>(msg: &str) {
    say_hello!(T);
}

fn main() {
    say_hello::<HelloWorld>("world");
}

Compiler error =>

error[E0599]: no function or associated item named `hello_world` found for type `T` in the current scope
  --> tests/dummy.rs:36:16
   |
31 | |         $generic::hello_world();
   | |_____________________________^
...
36 |       say_hello!(T);
   |  _____-----------^--
   | |     |
   | |     in this macro invocation

#2

The problem here is not the macro, but the code. You can just try to compile the same code without the macro and you will get the same function. The issue is that you have not told the compiler in any way that T has a function hello_world so when you do T::hello_world() it tells you exactly that. Potentially what you want is (I’ve intentionally kept your macro):

struct HelloWorld;

trait SayHello {
   fn hello_world() {
        println!("Hello world !!!");
    }
}

impl SayHello for HelloWorld {}

macro_rules! say_hello {
    ($generic:ty) => {
        $generic::hello_world();
    }
}

fn say_hello<T: SayHello>(msg: &str) {
    say_hello!(T);
}

fn main() {
    say_hello::<HelloWorld>("world");
}

Now this will not compile with the error:

error: expected expression, found `T`
  --> src/main.rs:13:9
   |
13 |         $generic::hello_world();
   |         ^^^^^^^^

Now this error is another beast and indeed rust does not allow to do this with macros as far as I’m aware.


#3

Indeed my bad, thanks for the correction.

I’ll try with “macro 2.0” then see if it works


#4

It looks like the compiler correctly points out the error. You want an expr, not a type. The two are subtly different here, which becomes more clear if you consider generics. The type Vec<u8> would be invalid if inserted here. Instead you would need Vec::<u8> which is an expression.


#5

The correct way to write that in a macro is:

<$generic>::hello_world();

https://play.rust-lang.org/?gist=cef440ca10163c2aa521e7ab547be3df

That works with types like Vec<u8> as well, as @droundy mentioned.

Even more correct, in case there is more than one hello_world method in scope:

<$generic as SayHello>::hello_world();

#6

Learning something new every day! Thanks for the explanation did not know about this.


#7

Thanks a lot @dtolnay