I'm playing around with the Rust macro system in order to learn more about how it works. I wrote a simple macro which takes an identifier and adds a list of numbers to it.
macro_rules! do_your_thing {
( $v:ident; $($e:expr),+ ) => {
let mut $v = 0;
$(
$v += $e;
)+
};
}
fn main() {
do_your_thing!(i; 3, 3, 3);
println!("{}", i);
}
If I run this program, the compiler will complain three times 'i' being an unresolved name for every repetition inside the macro for '$v += $e;'
<anon>:5:13: 5:15 error: unresolved name `i`
<anon>:5 $v += $e;
^
I know that macros in Rust are hygienic. That is why I used the ident
designator. Is it possible that there is an additional syntactic context for the repetition $(...)+
?
UPDATE
After DK.'s answer I did a little digging and found the hygiene
argument for the --pretty
option. Basically, it annotates the syntax contexts after macro expansion happened. After running
rustc -Z unstable-options --pretty expanded,hygiene main.rs
on my initial program it gave me the following output
fn main /* 67#0 */() {
let mut i /* 68#4 */ = 0;
i /* 68#3 */ += 3;
i /* 68#3 */ += 3;
i /* 68#3 */ += 3;
}
Running the same command on DK.'s modifications resulted in
fn main /* 67#0 */() {
let i /* 68#4 */ =
{
let mut i /* 68#5 */ = 0;
i /* 68#5 */ += 3;
i /* 68#5 */ += 3;
i /* 68#5 */ += 3;
i /* 68#5 */
};
}
So, the $(...)+ inside the macro did in fact introduce a new syntax context in my original macro. However, wrapping it in a block, as DK did, somehow prevented that from happening. Instead a new syntax context was introduced for the whole block.
答案 0 :(得分:7)
好的,这个很奇怪。首先,这是我发现的工作:
macro_rules! do_your_thing {
( $v:ident; $($e:expr),+ ) => {
let mut $v = {
let mut $v = 0;
$(
$v += $e;
)+
$v
};
};
}
fn main() {
do_your_thing!(i; 3, 3, 3);
println!("{}", i);
}
我可以说,问题在于你的原始宏正在生成一组语句,这是以某种方式混淆了编译器。将这些语句包装在一个块中似乎可以解决这个问题。
当然,然后问题是将let mut $v
放入作用域使得以下println!
无法访问它,所以我也修改它以返回最终值该块,然后分配给 new $v
。
老实说,我想不出为什么你的原始代码不应该有效。 可能是一个错误...或者它可能是macro_rules!
的另一个微妙之处,我还没有掌握。这很难说。 :)