A declarative macro uses `macro_rules!` to match input syntax and produce code at compile time. Its rules pair patterns with expansion templates, letting one macro handle different kinds of input. section 5.1.1
Keep the mechanics and tradeoffs in view:
- Call a macro with `!`; the delimiter group can use parentheses, braces, or brackets. section 5.1.1
- Rules can capture code fragments such as expressions and statements. Inputs that match no rule produce a compiler error. section 5.1.1
- Use macros when substantial code repeats with only small variations. Avoid adding one when ordinary code is already clear; overly complex macros can become difficult to work with. section 5.1 section 5.1.4
macro_rules! print_what_it_is {
() => { #1
println!("A macro with no arguments")
};
($e:expr) => { #2
println!("A macro with an expression")
};
($s:stmt) => { #3
println!("A macro with a statement")
};
}
See rules capture no input, an expression, or a statement, then expand to different output. section 5.1.1
For repeated implementations, a macro can generate structs and their methods from a name, or add a shared trait implementation. Declarative macros have limits; procedural macros are an alternative for problems of greater complexity. section 5.1.4
macro_rules! dog_struct {
($breed:ident) => {
struct $breed {
name: String,
age: i32,
breed: String, #1
}
impl $breed {
fn new(name: &str, age: i32) -> Self {
Self {
name: name.into(),
age,
breed: stringify!($breed).into(), #2
}
}
}
};
}
dog_struct!(Labrador);
dog_struct!(Golden);
dog_struct!(Poodle);
A breed identifier supplies the varying name while the macro generates a struct and constructor. section 5.1.4


