我有标识的名单,我想调用宏为每一对标识符从该列表。举例来说,如果我有a
,b
和c
,我想生成此:
println!("{} <-> {}", a, a);
println!("{} <-> {}", a, b);
println!("{} <-> {}", a, c);
println!("{} <-> {}", b, a);
println!("{} <-> {}", b, b);
println!("{} <-> {}", b, c);
println!("{} <-> {}", c, a);
println!("{} <-> {}", c, b);
println!("{} <-> {}", c, c);
当然,这是一个虚拟的例子。在我真正的代码,该标识符的类型,我想生成impl
块或类似的东西。
我的目标是列出每个标识符只有一次。在我真正的代码,我有大约12标识符,并且不希望手动写下所有12²= 144对。所以,我认为,宏可能会帮助我。我知道,它可以与所有强大的宏程序来解决,但我希望它也可以用声明宏(macro_rules!
)。
我尝试了我认为处理这个(两个嵌套的“循环”)(Playground)的直观的方式:
macro_rules! print_all_pairs {
($($x:ident)*) => {
$(
$(
println!("{} <-> {}", $x, $x); // `$x, $x` feels awkward...
)*
)*
}
}
let a = 'a';
let b = 'b';
let c = 'c';
print_all_pairs!(a b c);
然而,这将导致该错误:
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
--> src/main.rs:4:14
|
4 | $(
| ______________^
5 | | println!("{} <-> {}", $x, $x);
6 | | )*
| |_____________^
我想这是一种感觉,所以我尝试别的东西(Playground):
macro_rules! print_all_pairs {
($($x:ident)*) => {
print_all_pairs!(@inner $($x)*; $($x)*);
};
(@inner $($x:ident)*; $($y:ident)*) => {
$(
$(
println!("{} <-> {}", $x, $y);
)*
)*
};
}
但是,这会导致同样的错误如上!
这是可能的,在所有声明宏?
这是可能的,在所有声明宏?
是。
但是,(据我所知)我们必须经由头/尾递归列表迭代一次,而不是使用内置的$( ... )*
机制无处不在。这意味着该清单的长度由宏扩展的递归深度的限制。这是不是“唯一”的12项问题,虽然。
在下面的代码,我通过使宏名称与宏for_all_pairs
分离从打印代码的“对于所有对”功能。 (Playground)。
// The macro that expands into all pairs
macro_rules! for_all_pairs {
($mac:ident: $($x:ident)*) => {
// Duplicate the list
for_all_pairs!(@inner $mac: $($x)*; $($x)*);
};
// The end of iteration: we exhausted the list
(@inner $mac:ident: ; $($x:ident)*) => {};
// The head/tail recursion: pick the first element of the first list
// and recursively do it for the tail.
(@inner $mac:ident: $head:ident $($tail:ident)*; $($x:ident)*) => {
$(
$mac!($head $x);
)*
for_all_pairs!(@inner $mac: $($tail)*; $($x)*);
};
}
// What you actually want to do for each pair
macro_rules! print_pair {
($a:ident $b:ident) => {
println!("{} <-> {}", $a, $b);
}
}
// Test code
let a = 'a';
let b = 'b';
let c = 'c';
for_all_pairs!(print_pair: a b c);
此代码打印:
a <-> a
a <-> b
a <-> c
b <-> a
b <-> b
b <-> c
c <-> a
c <-> b
c <-> c