Macro in Rust

编译第一步

Rust 编译的第一步会把所有表达式拆成 token,构成一个 token tree.

比如:

  • 标识符(identifiers):foo, Bambous, self, we_can_dance, LaCaravane, …
  • 整数(integers):42, 72u32, 0_______0, …
  • 关键词(keywords):_, fn, self, match, yield, macro, …
  • 生命周期(lifetimes):'a, 'b, 'a_rare_long_lifetime_name, …
  • 字符串(strings):"", "Leicester", r##"venezuelan beaver"##, …
  • 符号(symbols):[, :, ::, ->, @, <-, …

    不过也有关键词

  • # [ $arg ]; 如 #[derive(Clone)], #[no_mangle], …

  • # ! [ $arg ]; 如 #![allow(dead_code)], #![crate_name="blang"], …
  • $name ! $arg; 如 println!("Hi!"), concat!("a", "b"), …
  • $name ! $arg0 $arg1; 如 macro_rules! dummy { () => {}; }.

后两种我们可以用 Macro-By-Example, 前两种接近 Rust 语法,需要用过程宏

Macro By Example

  • item: 条目,比如函数、结构体、模组等。
  • block: 区块(即由花括号包起的一些语句加上/或是一项表达式)。
  • stmt: 语句
  • pat: 模式
  • expr: 表达式
  • ty: 类型
  • ident: 标识符
  • path: 路径 (例如 foo, ::std::mem::replace, transmute::<_, int>, …)
  • meta: 元条目,即被包含在 #[...]#![...]属性内的东西。
  • tt: 标记树

我们可以用这个来进行捕获和重试:

macro_rules! vec_strs {
(
// 重复开始:
$(
// 每次重复必须有一个表达式...
$element:expr
)
// ...重复之间由“,”分隔...
,
// ...总共重复0或多次.
*
) => {
// 为了能包含多条语句,
// 我们将扩展部分包裹在花括号中...
{
let mut v = Vec::new();
// 重复开始:
$(
// 每次重复将包含如下元素,其中
// “$element”将被替换成其相应的展开...
v.push(format!("{}", $element));
)*
v
}
};
}