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_export]
macro_rules! foo {
    (@as_expr $e:expr) => {$e};

    ($($tts:tt)*) => {
        foo!(@as_expr $($tts)*)
    };
}Run

宏并不参与标准的条目可见性与查找流程,因此,如果一个公共可见宏在其内部调用了其它宏,那么被调用的宏也将必须公共可见。这会污染全局命名空间,甚至会与来自其它crate的宏发生冲突。那些想对宏进行选择性导入的用户也会因之感到困惑;他们必须导入所有宏——包括公开文档并未记录的——才能使代码正常运转。

将这些本不该公共可见的宏封装进需要被导出的宏内部,是一个不错的解决方案。上例展示了如何将常用的as_expr!宏移至另一个宏的内部,仅有后者公共可见。

之所以用@,是因为在Rust 1.2下,该标记尚无任何在前缀位置的用法;因此,我们的语法定义不会与任何东西撞车。想用的话,别的符号或特有前缀都可以;但@的用例已被传播开来,因此,使用它可能更容易帮助读者理解你的代码。

注意:标记@先前曾作为前缀被用于表示被垃圾回收了的指针,那时的语言还在采用各种记号代表指针类型。现在的标记@只有一种用法:将名称绑定至模式中。而在此用法中它是中缀运算符,与我们的上述用例并不冲突。

还有一点,内用规则通常应排在“真正的”规则之前。这样做可避免macro_rules!错把内规调用解析成别的东西,比如表达式。

如果导出内用规则无法避免(比如说,有一干效用性的宏规则,很多应被导出的宏都同时需要用到它们),你仍可以采用此规则,将所有内用规则封装到一个“究极”效用宏里去:

macro_rules! crate_name_util {
    (@as_expr $e:expr) => {$e};
    (@as_item $i:item) => {$i};
    (@count_tts) => {0usize};
    // ...
}Run