liveBook · Manning

Macro

As explained in Learn Rust in a Month of Lunches

A Rust macro generates or transforms source code during compilation.

A Rust macro writes code for you. You have probably used one already: `println!` is a macro, and the `!` marks a macro call. Unlike a regular function, a macro can produce code—such as a struct definition—for the compiler to check afterward. That makes macros useful when a function cannot remove repetitive typing. section 1.3 section 22.1

macro_rules! give_six {
    () => {
        6
    };
}
 
fn main() {
    let six = give_six!();
    println!("{}", six);
}

Start small: `macro_rules!` defines `give_six`. Its `()` rule accepts an empty call, and `give_six!()` produces the expression `6`. section 22.2

The rules resemble match arms, but they match input tokens rather than running a `match` expression. Different rules can produce different kinds of code. If a call fits no rule, the compiler reports an error such as “no rules expected the token.” And if a rule produces invalid Rust, the error appears when that macro is called—not merely when it is defined. section 22.2

Once literal inputs make sense, give parts of the input names:

  • `$input:expr` captures an expression; `$input:stmt` captures a statement instead. A `let` statement does not fit an `expr` rule. section 22.2
  • `$name:ident` captures an identifier, while `$input:tt` captures a token tree. These names can be used in the code the rule produces. section 22.2
  • `$($input:tt),*` accepts zero or more comma-separated inputs; changing `*` to `+` requires at least one. section 22.2
macro_rules! make_a_function {
    ($name:ident, $($input:tt),+) => {                        #1
        fn $name() {
            let output = stringify!($($input),+);             #2
            println!("{}", output);
        }
    };
}
 
fn main() {
    make_a_function!(print_it, 5, 5, 6, I);                   #3
    print_it();
    make_a_function!(say_its_nice, this, is, really, nice);   #4
    say_its_nice();
}

Here the identifier becomes a function name, and repeated inputs become its printed text. Two calls generate two functions without writing each function by hand. section 22.2

Reading a macro can also clarify how to call it. The standard-library `write!` rule accepts a destination followed by any number of token trees; `todo!` has rules for an empty call or supplied arguments. Some library macros eventually reach compiler-built-in parts whose details are not visible, so understanding their accepted inputs is a useful stopping point. section 22.3

macro_rules! derive_try_from {
    ($type:ident, $length:expr) => {
        impl TryFrom<&str> for $type {
            type Error = String;                                      #1
 
            fn try_from(value: &str) -> Result<Self, Self::Error> {
                let length = $length;                                 #2
 
                if value.chars().count() > length {
                    Err(format!("Must be no longer than {length}"))
                } else {
                    Ok(Self(value.to_string()))
                }
            }
        }
    };
}

A practical use: this rule takes a type name and a maximum length, then generates a `TryFrom<&str>` implementation. Calls for several string-holder types can share the same conversion logic. section 22.4

Macros are common to use but rarely necessary to write. Reach for one when it can generate repeated code that an ordinary function cannot; learn the syntax by reading existing rules and modifying small examples. section 22.4 section 22.5

How Learn Rust in a Month of Lunches covers it

How deep it goes
Works through macro_rules! patterns, fragment types, repetition, compiler errors, and library definitions, stopping where compiler-built-in macros hide details.
Examples
Shows println!, a macro returning six, generated functions, standard-library rules, and repeated string-holder conversions.
What it assumes
Teaches macro syntax from a minimal example, then uses familiarity with structs and TryFrom for the larger application.
What you can do after
Build it from scratch: write basic macro_rules! rules, read accepted inputs, and generate repeated Rust definitions.
How it connects
Connects macros to functions, tokens, match expressions, const generics, and the TryFrom trait.

Also explained in

Compare how 9 books explain macro →

Related concepts

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