4

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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mike Nishizawa
  • 1,410
  • 1
  • 14
  • 14
  • 2
    No this is not possible. Rust function names do not exist at runtime. You will need to build/use some for of reflection. – user1937198 Nov 15 '20 at 00:48
  • 1
    The `let fnName = "MyFunction"` is misleading. If you are instead asking if a macro can read a config file to get an ident, then the question becomes possible through procedural macros, `build.rs`, or potentially some sorcery with `include_bytes!` (but it will be very difficult). But the configuration file must be parsed at compile time! – Locke Nov 15 '20 at 00:50
  • 2
    I'm sure there is a better way to solve your problem than with macros. You could parse a command into an `enum`, which represents the different command implementations. Or you could store functions in a HashMap and look them up by name. In dynamic languages, which let you call functions by name, it would likely be implemented behind the scenes, in the language itself, using something like a HashMap. – Peter Hall Nov 15 '20 at 01:18
  • 2
    I'd definitely encourage the build script route. – Shepmaster Nov 15 '20 at 01:20
  • 2
    See also [Calling a function only known at runtime](https://stackoverflow.com/q/30540807/155423); [How to dynamically build function calls with different numbers of arguments in Rust?](https://stackoverflow.com/q/56856382/155423). – Shepmaster Nov 15 '20 at 01:25
  • Thank you for all the comments. Very informative. – Mike Nishizawa Nov 16 '20 at 04:59

1 Answers1

7

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.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204