1. 1. Introduction
  2. 2. 宏,彻底剖析
    1. 2.1. 语法扩展
      1. 2.1.1. 源码解析过程
      2. 2.1.2. AST中的宏
      3. 2.1.3. 展开
    2. 2.2. macro_rules!
    3. 2.3. 细枝末节
      1. 2.3.1. 再探捕获与展开
      2. 2.3.2. 卫生性
      3. 2.3.3. 不是标识符的标识符
      4. 2.3.4. 调试
      5. 2.3.5. 作用域
      6. 2.3.6. 导入/导出
  3. 3. 宏,实践介绍
  4. 4. 常用模式
    1. 4.1. 回调
    2. 4.2. 标记树撕咬机
    3. 4.3. 内用规则
    4. 4.4. 下推累积
    5. 4.5. 重复替代
    6. 4.6. 尾部分隔符
    7. 4.7. 标记树聚束
    8. 4.8. 可见性
    9. 4.9. 临时措施
  5. 5. 轮子
    1. 5.1. AST强转
    2. 5.2. 计数
    3. 5.3. 枚举解析
  6. 6. 实例注解
    1. 6.1. Ook!

AST中的宏

如前所述,在Rust中,宏处理发生在AST生成之后。因此,调用宏的语法必须是Rust语言语法中规整相符的一部分。实际上,Rust语法包含数种“语法扩展”的形式。我们将它们同用例列出如下:

头两种形式被称作“属性(attribute)”,被同时用于语言特属的结构(比如用于要求兼容C的ABI的#[repr(C)])以及语法扩展(比如 #[derive(Clone)])。当前没有办法定义这种形式的宏。

我们感兴趣的是第三种:我们通常使用的宏正是这种形式。注意,采用这种形式的并非只有宏:它是一种一般性的语法扩展形式。举例来说,format! 是宏,而format_args! (它被用于format!) 并不是。

第四种形式实际上宏无法使用。事实上,这种形式的唯一用例只有 macro_rules! 我们将在稍后谈到它。

将注意力集中到第三种形式 ($name ! $arg)上,我们的问题变成,对于每种可能的语法扩展,Rust的语法分析器(parser)如何知道$arg究竟长什么样?答案是它不需要知道。其实,提供给每次语法扩展调用的参数,是一棵标记树。具体来说,一棵非叶节点的标记树;即(...)[...],或{...}。拥有这一知识后,语法分析器如何理解如下调用形式,就变得显而易见了:

bitflags! {
    flags Color: u8 {
        const RED    = 0b0001,
        const GREEN  = 0b0010,
        const BLUE   = 0b0100,
        const BRIGHT = 0b1000,
    }
}

lazy_static! {
    static ref FIB_100: u32 = {
        fn fib(a: u32) -> u32 {
            match a {
                0 => 0,
                1 => 1,
                a => fib(a-1) + fib(a-2)
            }
        }

        fib(100)
    };
}

fn main() {
    let colors = vec![RED, GREEN, BLUE];
    println!("Hello, World!");
}Run

虽然看起来上述调用包含了各式各样的Rust代码,但对语法分析器来说,它们仅仅是堆毫无意义的标记树。为了让事情变得更清晰,我们把所有这些句法“黑盒”用⬚代替,仅剩下:

bitflags! ⬚

lazy_static! ⬚

fn main() {
    let colors = vec! ⬚;
    println! ⬚;
}

再次重申,语法分析器对⬚不作任何假设;它记录黑盒所包含的标记,但并不尝试理解它们。

需要记下的点:

脚注: 接下来(包括下一节)将提到的某些内容将适用于一般性的语法扩展。1

最后一点最为重要,它带来了一些深远的影响。由于宏将被解析进AST中,它们将仅仅只能出现在那些支持它们出现的位置。具体来说,宏能在如下位置出现:

一些并不支持的位置包括:

绝对没有任何在上述位置以外的地方使用宏的可能。


  1. 这样比较方便,因为“宏”打起来比“语法扩展”更快更简单。 

  2. 在非稳定Rust中可以通过#![feature(type_macros)]使用类型宏。见Issue #27336。