Simplifying macro rules


#1

I have a simple macro with three very similar rules:

macro_rules! c {

    ($exp:expr, for $i:ident in $iter:expr) => (
        {
            let mut r = vec![];
            for $i in $iter {
                r.push($exp);
            }
            r
        }
    );
    ($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr) => (
        {
            let mut r = vec![];
            for $i2 in $iter2 {
                for $i in $iter {
                    r.push($exp);
                }
            }
            r
        }
    );
    ($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr, for $i3:ident in $iter3:expr) => (
        {
            let mut r = vec![];
            for $i in $iter {
                for $i2 in $iter2 {
                    for $i3 in $iter3 {
                        r.push($exp);
                    }
                }
            }
            r
        }
    );

}

Each rule differs from the others by the number of for $i:ident in $iter:exp being matched. The logic is similarly the same.

Is there a way to simplify these rules into one using repetition patterns such as $(...)* or $(...)+ and still be able to express the nested looping in the macro logic?

In my attempts I kept getting an error error: variable 'i' is still repeating at this depth.

Playground link


#2

I don’t know if this is what you would consider “simpler” but a recursive macro would work, where it builds up the layers of for loops one at a time.

macro_rules! c {
    ($e:expr $(, for $i:ident in $iter:expr)+) => {{
        let mut r = vec![];
        c!(@loop r.push($e) $(, for $i in $iter)*);
        r
    }};
    (@loop $e:expr, for $i:ident in $iter:expr $(, for $j:ident in $rest:expr)*) => {
        c!(@loop for $i in $iter { $e } $(, for $j in $rest)*);
    };
    (@loop $e:expr) => {
        $e
    };
}

#3

Thanks @dtolnay, this seems to do the trick :slightly_smiling_face: