bitflags 在宏三大法器中抢夺出了前四名中的一席之地。它本身的功能相当简单。直接看代码:

bitflags::bitflags! {
    pub struct Flags: u32 {
        const A = 0b00000001;
        const B = 0b00000010;
        const C = 0b00000100;
    }
}

或是

pub struct Flags(u32);

bitflags! {
    impl Flags: u32 {
        const A = 0b00000001;
        const B = 0b00000010;
        const C = 0b00000100;
    }
}

然后就可以把 Flags 类似 u128 一样计算使用,不同的是它基于 Flags trait 实现了一些好方法,如同官方 slice 一样,让我们能够轻松使用。

这是一种值得学习的范式:用 declarative macro 实现最小化扩展,然后用一个 trait 实现更多方法。

让我们深入看看 bitflags 的实际实现:教科书式的 declarative macro。

$(#[$outer:meta])*
$vis:vis struct $BitFlags:ident: $T:ty {
    $(
        $(#[$inner:ident $($args:tt)*])*
        const $Flag:tt = $value:expr;
    )*
}

$($t:tt)*

首先它处理了内外的 metadata;以支持诸如嵌套 macro、文档注释、编译标记等额外信息。

然后是逐步的函数式 declarative macro 实现。

它在最后处理了额外的 Token Tree,以支持多类型定义。

总之简单好用就完了。