我在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;
}
}
我希望将其扩展为
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),*);
有办法做我想做的事吗?
答案 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 作为单个标记,以便在第一次扩展中仅重复函数名称。