Skip to content

G.MAC.DCL.01 编写宏匹配规则时,宜遵从匹配范围从小至大的顺序

【级别】 建议

【描述】

因为声明宏中,是按规则的编写顺序来匹配的。当第一个规则被匹配到,后面的规则将永远不会匹配 到。

所以,编写声明宏规则时,需要先写匹配范围最小的,最具体的规则,然后逐步编写匹配范围更广泛的规则。

参考 The Little Book of Rust Macros

【反例】

Rust
// 不符合, `$($other:tt)*` 匹配条件在这个宏定义中匹配范围最大却放在了第一位。
macro_rules! match_tokens {
  // 可能会导致调用时代码被错误地匹配到了这一条规则。
  ($($other:tt)*) => {"got something else"}; 
  ($a:tt + $b:tt) => {"got an addition"};
  ($i:ident) => {"got an identifier"}; 
}

// 以下的三种语法: `caravan`, `3 + 6`, `5` 均被匹配为该宏的同一条规则
assert_eq!(
    format!("{},{},{}",
      match_tokens!(caravan), 
      match_tokens!(3 + 6),
      match_tokens!(5)
  ),
  "got something else,got something else,got something else"
);

【正例】

Rust
// 符合,匹配范围从小到大
macro_rules! match_tokens {
  ($a:tt + $b:tt) => {"got an addition"}; 
  ($i:ident) => {"got an identifier"};
  ($($other:tt)*) => {"got something else"}; 
}

// 匹配合理,宏将 `caravan` 识别为 identifier, `3 + 6` 识别为 addition, `5` 识别为 something else
assert_eq!(
  format!("{},{},{}",
    match_tokens!(caravan), 
    match_tokens!(3 + 6),
    match_tokens!(5)
  ),
  "got an identifier,got an addition,got something else"
);