尽管'ident'指示符,但宏中的"未解析的名称"

jte*_*epe 7 rust

我正在玩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一样将它包裹在一个区块中,以某种方式阻止了这种情况的发生.而是为整个块引入了新的语法上下文.

DK.*_*DK. 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);
}
Run Code Online (Sandbox Code Playgroud)

我可以说,问题在于你的原始宏正在生成一组语句,这在某种程度上让编译器感到困惑.将这些语句包装在一个块中似乎可以解决这个问题.

当然,随后的问题是,将let mut $v进入一个范围就无法访问了以下内容println!,所以我还修改了它从块,然后将其分配到返回的最终值 $v.

老实说,我想不出为什么你的原始代码不应该有效.它可能是一个错误...或者它可能是另一个微妙的macro_rules!我还没有掌握.这很难说.:)