在嵌套的 Rust 宏中动态创建参数

Dynamically creating parameters in nested rust macros

我研究 Rust 的宏系统已经有一段时间了,最​​近对将两个宏嵌套在一起产生了兴趣,就像这样:

macro_rules! foo {
    () => {
        macro_rules! bar {
            () => {}
        }
    }
}

关于这个例子,我想在 bar! 中动态创建传递给 foo! 的参数名称,以获得这样的结果:

foo!(bar, baz); 

// The above call creates a macro, bar!, with the following definition:
macro_rules! bar {
    ( $bar:literal, $baz:literal ) => {
        println!("{}", stringify!( $bar, $baz ));
    }
}

为了更好地了解我正在尝试做的事情,这是我对它如何工作的初步思考过程(这应该完全解析为上面显示的定义):

macro_rules! foo {
    ( $( $attr:ident ), * ) => {
        macro_rules! bar {
            // the designator $$attr:literal consists of two parts - $attr,
            // which should be replaced with the arguments passed into foo!,
            // and $__:literal, which creates a literal designator for each of
            // the arguments from foo! for bar!
            ( $( $$attr:literal ), * ) => {
                // $( $$attr ), * follows the same logic as above
                println!("{}", stringify!( $( $$attr ), * ));
            }
        }
    }
}

这看起来确实很奇怪,果然,它没有用,给出了一个错误提示meta-variable expressions and this issue, both of which looked unrelated (full error can be seen on the playground)。

有谁知道是否可以使用这样的变量动态创建宏,如果可以,该怎么做?

是的,但是...

您不能插入 $ 符号,因为它是为元变量保留的。

你有两种选择来解决这个问题。

在稳定版中,您需要将 $ 传递给宏。然后它可以使用元变量引用它。

macro_rules! foo {
    ( $dollar:tt $( $attr:ident ), * ) => {
        macro_rules! bar {
            ( $( $dollar $attr:literal ), * ) => {
                println!("{}", stringify!( $( $dollar $attr ), * ));
            }
        }
    }
}

foo!($ bar, baz);

Playground.

在夜间,您可以转义美元符号:这是编译器提到的 macro_metavar_expr 功能的一部分。您可以使用 $$:

#![feature(macro_metavar_expr)]

macro_rules! foo {
    ( $( $attr:ident ), * ) => {
        macro_rules! bar {
            ( $( $$ $attr:literal ), * ) => {
                println!("{}", stringify!( $( $$ $attr ), * ));
            }
        }
    }
}

foo!(bar, baz);

Playground.