Macros
Macro rules, also called macros by example, are a way to avoid code duplication by generating code at compile time. We will implement a simple macro to implement our BitSet trait for integer types:
macro_rules! int_bitset { ($ty:ty) => { impl BitSet for $ty { fn clear(&mut self, index: usize) { *self &= !(1 << index); } fn is_set(&self, index: usize) -> bool { (*self >> index) & 1 == 1 } fn set(&mut self, index: usize) { *self |= 1 << index; } } }; }
The name of the int_bitset macro is written after macro_rules!. A macro can have multiple rules, similar to match arms, but it matches on Rust syntactic elements instead, with types, expressions, blocks of code, and so on. Here we only have one rule and it matches against a single type since we use :ty. The part before :ty ($ty) is the name for the element that was matched. Inside the curly brackets, after the => symbol, we see the actual code that will be generated. It is the same as our previous implementation of BitSet for u64, except that it uses the meta-variable $ty instead of u64.
To avoid a lot of boilerplate code, we can then use this macro as follows:
int_bitset!(i32); int_bitset!(u8); int_bitset!(u64);