Nested macros issue


#1

According to this merged pull request into Rust it should support macros. It seems that it does support it, up to some extend however, specifically when needing list of expressions is where it fails. I wanted to know if I’m doing something wrong, if there is a better alternative. For my example, I’m trying to define a macro that defines the function and creates a same name macro evoking it (Note this is made for demonstration):

macro_rules! impl_a_method {
    ($fn_name: ident ( $( $e:ident : $e_ty:ty ) , *)
    -> $obj:ty {$body:expr}) => {
        fn $fn_name( $( $e : $e_ty ) , * ) -> $obj { $body }

        macro_rules! $fn_name {
            ( $( $ep : expr ) , * ) => (
                $fn_name( $( $ep ) , * )
            )
        }
    }
}

The compiler complains on the inner macro list expression with:

error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
  --> src/main.rs:22:15
   |
22 |             ( $( $ep : expr ) , * ) => (
   |               ^^^^^^^^^^^^^^^

So I was wondering if anyone can help me in how this can be implemented or what workarounds there might be?


#2

This is a limitation of nested macro_rules: rust-lang/rust#35853. One workaround is to avoid a repetition by unrolling as many times as you will need:

macro_rules! impl_a_method {
    ($n:ident ( $a:ident : $ta:ty ) -> $ret:ty { $body:expr }) => {
        fn $n($a:$ta) -> $ret { $body }
        macro_rules! $n { ($va:expr) => { $n($va) } }
    };
    ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty ) -> $ret:ty { $body:expr }) => {
        fn $n($a:$ta, $b:$tb) -> $ret { $body }
        macro_rules! $n { ($va:expr, $vb:expr) => { $n($va, $vb) } }
    };
    ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty ) -> $ret:ty { $body:expr }) => {
        fn $n($a:$ta, $b:$tb, $c:$tc) -> $ret { $body }
        macro_rules! $n { ($va:expr, $vb:expr, $vc:expr) => { $n($va, $vb, $vc) } }
    };
    ($n:ident ( $a:ident : $ta:ty, $b:ident : $tb:ty, $c:ident : $tc:ty, $d:ident : $td:ty ) -> $ret:ty { $body:expr }) => {
        fn $n($a:$ta, $b:$tb, $c:$tc, $d:$td) -> $ret { $body }
        macro_rules! $n { ($va:expr, $vb:expr, $vc:expr, $vd:expr) => { $n($va, $vb, $vc, $vd) } }
    };
}

impl_a_method! {
    plus(a: u8, b: u8) -> u8 { a + b }
}

fn main() {
    println!("{}", plus!(8, 13));
}

#3

Ok thanks a lot I guess I will have to use smth like that. Essentially, I wanted to proof a point that you can very easily do default parameters values (as in C++) in Rust via a macro.