liveBook · Manning

Declarative macro

As explained in Idiomatic Rust

Rust's macro_rules! syntax defines declarative macros by associating input token patterns with expansion templates.

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

How Idiomatic Rust covers it

How deep it goes
Explains matching rules, fragment captures, and unmatched-input errors, then defers more complex cases to procedural macros.
Examples
Demonstrates no-op and fragment-matching macros, then generates dog-breed structs and trait implementations.
What you can do after
Build it from scratch by mapping an identifier to repeated struct, constructor, and trait code.
How it connects
Connects macro expansion to code fragments, hygiene, traits, and procedural macros.

Also explained in

Compare how 3 books explain declarative macro →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.