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!

作用域

宏作用域的决定方式可能有一点反直觉。首先就与语言剩下的所有部分都不同的是,宏在子模组中仍然可见。

macro_rules! X { () => {}; }
mod a {
    X!(); // 已被定义
}
mod b {
    X!(); // 已被定义
}
mod c {
    X!(); // 已被定义
}Run

注意:即使子模组的内容处在不同文件中,这些例子中所述的行为仍然保持不变。

其次,同样与语言剩下的所有部分不同,宏只有在其定义之后可见。下例展示了这一点。同时注意到,它也展示了宏不会“漏出”其定义所在的域:

mod a {
    // X!(); // 未被定义
}
mod b {
    // X!(); // 未被定义
    macro_rules! X { () => {}; }
    X!(); // 已被定义
}
mod c {
    // X!(); // 未被定义
}Run

需要阐明的是,即便宏定义被移至外围域,此顺序依赖行为仍旧不变:

mod a {
    // X!(); // 未被定义
}
macro_rules! X { () => {}; }
mod b {
    X!(); // 已被定义
}
mod c {
    X!(); // 已被定义
}Run

然而,对于宏们自身来说,此依赖行为不存在:

mod a {
    // X!(); // 未被定义
}
macro_rules! X { () => { Y!(); }; }
mod b {
    // X!(); // 已被定义, 但Y!未被定义
}
macro_rules! Y { () => {}; }
mod c {
    X!(); // 均已被定义
}Run

可通过 #[macro_use]属性将宏导出模组:

mod a {
    // X!(); // 未被定义
}
#[macro_use]
mod b {
    macro_rules! X { () => {}; }
    X!(); // 已被定义
}
mod c {
    X!(); // 已被定义
}Run

注意到这一特性可能会产生一些奇怪的后果,因为宏中的标识符只有在宏展开的过程中才会被解析。

mod a {
    // X!(); // 未被定义
}
#[macro_use]
mod b {
    macro_rules! X { () => { Y!(); }; }
    // X!(); // 已被定义,但Y!并未被定义
}
macro_rules! Y { () => {}; }
mod c {
    X!(); // 均已被定义
}Run

让情形变得更加复杂的是,当#[macro_use]被作用于extern crate时,其行为又会发生进一步变化:此类声明从效果上看,类似于被放在了整个模组的顶部。因此,假设在某个extern crate mac中定义了X!,则有:

mod a {
    // X!(); // 已被定义,但Y!并未被定义
}
macro_rules! Y { () => {}; }
mod b {
    X!(); // 均已被定义
}
#[macro_use] extern crate macs;
mod c {
    X!(); // 均已被定义
}Run

最后,注意这些有关作用域的行为同样适用于函数,除了#[macro_use]以外(它并不适用):

macro_rules! X {
    () => { Y!() };
}

fn a() {
    macro_rules! Y { () => {"Hi!"} }
    assert_eq!(X!(), "Hi!");
    {
        assert_eq!(X!(), "Hi!");
        macro_rules! Y { () => {"Bye!"} }
        assert_eq!(X!(), "Bye!");
    }
    assert_eq!(X!(), "Hi!");
}

fn b() {
    macro_rules! Y { () => {"One more"} }
    assert_eq!(X!(), "One more");
}Run

由于前述种种规则,一般来说,建议将所有应对整个crate均可见的宏的定义置于根模组的最顶部,借以确保它们一直可用。