In Rust, macros are commands that generate other Rust code. They are known by the trailing exclamation mark, like "println
!" We can create our own, custom macros.
With the macro_rules
macro, we create a macro. We use some complex syntax to specify an argument list of expressions and then can output code statements for each expression.
This program specifies a macro similar to the "vec
!" macro, but with a name of bird. So we can create a bird struct
with various attributes by calling "bird!"
macro_export
and a repeated expr to generate some code statements for each call to the "bird!" macro.struct
with a vector field. An impl
block contains the add_category()
function for the macro.class
is created, and the add_category()
called for each expr.// Part 1: specify a macro that generates some Rust statements. #[macro_export] macro_rules! bird { ( $( $x:expr ),* ) => { { let mut b = Bird { categories: Vec::new() }; // Part 2: this code is generated for each expression encountered. $( b.add_category($x); )* b } }; } // Part 3: the Bird class and an impl block that contains the method add_category. #[derive(Debug)] struct Bird { categories: Vec<usize>, } impl Bird { fn add_category(&mut self, c: usize) { self.categories.push(c); } } fn main() { // Part 4: use the new bird macro. let b = bird![10, 20, 100]; println!("{:?}", b); let b = bird![990]; println!("{:?}", b); }Bird { categories: [10, 20, 100] } Bird { categories: [990] }
It would be possible to create a special function that initializes and returns Bird structs, and avoid the macro. But the "bird!" macro is likely easier to specify and remember.
Macros have complex syntax that is unlike other Rust method syntax. They can lead to helpful features throughout programs, but can also be hard to develop and debug.