Rust宏中的嵌套迭代

时间:2016-06-10 15:37:52

标签: rust

我在Rust中使用宏并希望进行嵌套扩展,即组合。

这是我写的代码:

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

我希望将其扩展为

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);
}

然而,Rust似乎并不支持这一点,而是抱怨:

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

显然它忽略了内在的迭代。

然而,如果我删除其中一个args,为两个都做2个项目,它仍会抱怨:

<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),*);

有办法做我想做的事吗?

2 个答案:

答案 0 :(得分:8)

似乎不可能进行这种扩展。这是一个解决方法:

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),*);
    };
}

@id仅用于将规则internal保留在宏中。

答案 1 :(得分:0)

这里有一个稍微简单的方法。

macro_rules! nested {
    (($($f:ident),*) $args:tt) => {
        $(nested!(@call $f $args);)*
    };
    (@call $f:ident ($($arg:expr),*)) => {
        $f($($arg),*);
    };
}

nested! {
    (show1, show2)
    (a, b, c)
}

与@malbarbo 的答案相同的原则,但更改您的宏以将 args 放在括号中。然后,您可以接受 args 作为单个标记,以便在第一次扩展中仅重复函数名称。