Mike Nishizawa
Mike Nishizawa

Reputation: 2012

Is it possible to pass a variable as an identifier in a macro?

I want to build a macro that will create functions based upon a configuration file. I would like to be able to invoke that function by name at runtime by passing an argument i.e. a command pattern. For example:

macro_rules! create_fn {
    ($name:ident) => {
        fn $name() {
            println!("Hello from fn {}", stringify!($name));
        }
    };
}

fn main() {
    let fn_name = "MyFunction"; //this would come from a config file
    create_fn!(fn_name);
}

Upvotes: 5

Views: 3992

Answers (1)

Peter Hall
Peter Hall

Reputation: 58695

The short answer is no.

  • Macros only operate on tokens, so a macro could not find the value of a variable until the program is running
  • Function names must be known at compile time.
  • Declarative macros (those defined with macro_rules!) are just not powerful enough to do arbitrary token manipulation.

The longer answer is yes, but with a lot of limitations. Firstly, it would have to be a procedural macro, not a declarative one. Secondly, you'd have to pass the name of the file as a literal; it could not come from a variable.

I won't go into the details of implementing the procedural macro, but it would be used like this:

create_fn!("function_name.txt");

Where "function_name.txt" is a file, that is in your source code. A procedural macro could read the file and use a value from it to define a function.

But it would not be possible to write a macro that could be used like this:

let file = "function_name.txt";
create_fn!(file);

That's because the macro would only see file as its input. It would not be able to determine the value of that variable until the program actually runs, but the macro would need to know the name at compile-time in order to write the out the tokens.

This is very limited and it's hard to imagine it being useful in practice. Perhaps if the configuration contained functions written in a different language and you wanted the macro to parse them and translate them into Rust, then that could work. But I imagine there would be better ways of accomplishing that.

Upvotes: 7

Related Questions