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!

可见性

在Rust中,因为没有类似vis的匹配选项,匹配替换可见性标记比较难搞。

匹配与忽略

根据上下文,可由重复做到这点:

macro_rules! struct_name {
    ($(pub)* struct $name:ident $($rest:tt)*) => { stringify!($name) };
}Run

上例将匹配公共可见或本地可见的struct条目。但它还能匹配到pub pub (十分公开?)甚至是pub pub pub pub (真的非常非常公开)。防止这种情况出现的最好方法,只有祈祷调用方没那么多毛病。

匹配和替换

由于不能将重复的内容和其自身同时绑定至一个变量,没有办法将$(pub)*的内容直接拿去替换使用。因此,我们只好使用多条规则:

macro_rules! newtype_new {
    (struct $name:ident($t:ty);) => { newtype_new! { () struct $name($t); } };
    (pub struct $name:ident($t:ty);) => { newtype_new! { (pub) struct $name($t); } };
    
    (($($vis:tt)*) struct $name:ident($t:ty);) => {
        as_item! {
            impl $name {
                $($vis)* fn new(value: $t) -> Self {
                    $name(value)
                }
            }
        }
    };
}

macro_rules! as_item { ($i:item) => {$i} }Run

参考AST强转.

这里,我们用到了宏对成组的任意标记的匹配能力,来同时匹配()(pub),并将所得内容替换到输出中。因为在此处解析器不会期望看到一个tt重复的展开结果,我们需要使用AST强转来使代码正常运作。