可见性

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

匹配与忽略

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

  1. macro_rules! struct_name {
  2. ($(pub)* struct $name:ident $($rest:tt)*) => { stringify!($name) };
  3. }
  4. #
  5. # fn main() {
  6. # assert_eq!(struct_name!(pub struct Jim;), "Jim");
  7. # }

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

匹配和替换

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

  1. macro_rules! newtype_new {
  2. (struct $name:ident($t:ty);) => { newtype_new! { () struct $name($t); } };
  3. (pub struct $name:ident($t:ty);) => { newtype_new! { (pub) struct $name($t); } };
  4. (($($vis:tt)*) struct $name:ident($t:ty);) => {
  5. as_item! {
  6. impl $name {
  7. $($vis)* fn new(value: $t) -> Self {
  8. $name(value)
  9. }
  10. }
  11. }
  12. };
  13. }
  14. macro_rules! as_item { ($i:item) => {$i} }
  15. #
  16. # #[derive(Debug, Eq, PartialEq)]
  17. # struct Dummy(i32);
  18. #
  19. # newtype_new! { struct Dummy(i32); }
  20. #
  21. # fn main() {
  22. # assert_eq!(Dummy::new(42), Dummy(42));
  23. # }

参考AST强转.

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