Search code examples
rustrust-macros

Rust macro to generate a macro


I'm writing some macros that are all really similar (MWE the actual ones are much larger):

macro_rules! var1 {
  () => {some_func(some_enum::Var1, vec![])};
  ($($e:expr),*) => {some_func(some_enum::Var1, vec![$($s),*])};
}
macro_rules! var2 {
  () => {some_func(some_enum::Var2, vec![])};
  ($($e:expr),*) => {some_func(some_enum::Var2, vec![$($s),*])};
}

I instead decided to try different brackets

macro_rules! var {
  () => {some_func(some_enum::Var1, vec![])};
  ($($e:expr),*) => {some_func(some_enum::Var1, vec![$($s),*])};
  [] => {some_func(some_enum::Var2, vec![])};
  [$($e:expr),*] => {some_func(some_enum::Var2, vec![$($s),*])};
}

which didn't work. This would have been my preferred method to do this so if you know how to do this that's be greatly appreciated.

I instead tried to use a macro to generate the other macros:

macro_rules! generator {
  ($t:ident, $s:expr) => {
    macro_rules! $t {
      () => {some_func($s:expr, vec![])};
      ($($e:expr),*) => {some_func($s:expr, vec![$($s),*])};
    }
  };
}
generator!(var1, some_enum::Var1);
generator!(var2, some_enum::Var2);

This gives me the error "attempted to repeat an expression containing no syntax variables matched as repeating at this depth" andx I don't know what it means.


Solution

  • The error you're getting means that you use the repeat syntax $(),* without a syntax variable in it, which you see when you look at the full error message:

    error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
      --> src/main.rs:10:9
       |
    10 |       ($($e:expr),*) => {some_func($s:expr, vec![$($e),*])};
       |         ^^^^^^^^^
    

    The problem is you try to use the special syntax to define syntax variables inside of a macro where it has some different special meaning. You can get around it by simply escaping the $ that you do not wish to expand on the first macro invocation:

    macro_rules! generator {
      ($t:ident, $s:expr) => {
        macro_rules! $t {
          () => {some_func($s, vec![])};
          ($$($$e:expr),*) => {some_func($s, vec![$$($$e),*])};
        }
      }
    }
    

    But macro_metavar_expr isn't stable yet so you have to add #![feature(macro_metavar_expr)] at the top of your crate and compile with a nightly compiler.

    Note: added ; after the macro branches and fixed some other syntax errors in there as well.