Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested iteration in Rust macros

Tags:

rust

I'm playing with macros in Rust and want to do nested expansion, i.e. combinatorics.

This is the code I've written:

macro_rules! nested {
    (
        $(arg $arg:ident;)*
        $(fun $fun:ident;)*
    ) => {
        $(
            $fun($($arg),*);
        )*
    }
}

fn show1(a: i32, b: i32, c: i32) {
    println!("show1: {} {} {}", a, b, c);
}
fn show2(a: i32, b: i32, c: i32) {
    println!("show2: {} {} {}", a, b, c);
}

fn main() {
    let a = 1;
    let b = 2;
    let c = 3;
    nested! {
        arg a;
        arg b;
        arg c;
        fun show1;
        fun show2;
    }
}

Playground

I want this to expand to

fn main() {
    let a = 1;
    let b = 2;
    let c = 3;
    // iteration over $fun
    show1(/* iteration over $arg */a, b, c);
    show2(/* iteration over $arg */a, b, c);
}

However, it seems that Rust doesn't support this and instead complains:

error: inconsistent lockstep iteration: 'fun' has 2 items, but 'arg' has 3

So apparently it ignores the inner iteration.

However, if I remove one of the args, to make it 2 items for both, it still complains:

<anon>:7:18: 7:25 error: attempted to repeat an expression containing no
                  syntax variables matched as repeating at this depth
<anon>:7             $fun($($arg),*);

Is there a way to do what I want?

like image 988
Sebastian Redl Avatar asked Jun 10 '16 15:06

Sebastian Redl


Video Answer


1 Answers

It seems that it is not possible to do this kind of expansion. Here is a workaround:

macro_rules! nested {
    ($(arg $arg:ident;)* $(fun $fun:ident;)*) => {
        // expand arg to a tuple that will be matched as tt
        // in @call_tuple an will be decomposed back to
        // a list of args in @call
        nested!(@call_tuple $($fun),* @ ($($arg),*))
    };
    (@call_tuple $($fun:ident),* @ $tuple:tt) => {
        $(nested!(@call $fun $tuple))*
    };
    (@call $fun:ident ($($arg:expr),*)) => {
        $fun($($arg),*);
    };
}

The @id is only used to keep the rules internal to the macro.

like image 60
malbarbo Avatar answered Sep 20 '22 11:09

malbarbo