我正在玩Rust宏系统,以便了解它的工作原理.我写了一个简单的宏,它接受一个标识符并添加一个数字列表.
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);
}
Run Code Online (Sandbox Code Playgroud)
如果我运行这个程序,编译器会为'$ v + = $ e'中宏的内部每次重复都抱怨三次'i'是一个未解析的名字.
<anon>:5:13: 5:15 error: unresolved name `i`
<anon>:5 $v += $e;
^
Run Code Online (Sandbox Code Playgroud)
我知道Rust中的宏是卫生的.这就是我使用ident
指示符的原因.是否有可能为重复提供额外的句法上下文$(...)+
?
UPDATE
在DK.的答案之后,我做了一点挖掘并找到了hygiene
该--pretty
选项的论据.基本上,它在宏扩展发生后注释语法上下文.跑完之后
rustc -Z unstable-options --pretty expanded,hygiene main.rs
Run Code Online (Sandbox Code Playgroud)
在我的初始程序中,它给了我以下输出
fn main /* 67#0 */() {
let mut i /* 68#4 */ = 0;
i /* 68#3 */ += 3;
i /* 68#3 */ += 3;
i /* 68#3 */ += 3;
}
Run Code Online (Sandbox Code Playgroud)
对DK.的修改运行相同的命令导致
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 */
};
}
Run Code Online (Sandbox Code Playgroud)
所以,宏中的$(...)+确实在我的原始宏中引入了一个新的语法上下文.然而,像DK一样将它包裹在一个区块中,以某种方式阻止了这种情况的发生.而是为整个块引入了新的语法上下文.
好的,这个很奇怪.首先,这是我发现的工作:
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);
}
Run Code Online (Sandbox Code Playgroud)
我可以说,问题在于你的原始宏正在生成一组语句,这在某种程度上让编译器感到困惑.将这些语句包装在一个块中似乎可以解决这个问题.
当然,随后的问题是,将let mut $v
进入一个范围就无法访问了以下内容println!
,所以我还修改了它从块,然后将其分配到返回的最终值新 $v
.
老实说,我想不出为什么你的原始代码不应该有效.它可能是一个错误...或者它可能是另一个微妙的macro_rules!
我还没有掌握.这很难说.:)