%内用规则

    1. #[macro_export]
    2. macro_rules! foo {
    3. (@as_expr $e:expr) => {$e};
    4. ($($tts:tt)*) => {
    5. foo!(@as_expr $($tts)*)
    6. };
    7. }
    8. #
    9. # fn main() {
    10. # assert_eq!(foo!(42), 42);
    11. # }

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

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

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

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

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

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

    1. macro_rules! crate_name_util {
    2. (@as_expr $e:expr) => {$e};
    3. (@as_item $i:item) => {$i};
    4. (@count_tts) => {0usize};
    5. // ...
    6. }