Course contentsShow
← Rust
Lesson 1532 of 1,91013. Metaprogramming with MacrosFree lesson

Optimizing Macro Performance

You will minimize expansion overhead to keep compilation times within acceptable limits.

In this lesson, you will learn how to reduce compilation bottlenecks by minimizing the depth and complexity of your macro expansions.

The Cost of Expansion

Every time you invoke a macro, the Rust compiler must parse the input tokens, expand them into new code, and perform recursive expansions if your macro calls others. When you have a high volume of calls or deeply nested expansions, the compiler’s work grows non-linearly. To keep compilation times within acceptable limits, we must treat macro expansion as a "pay-as-you-go" cost.

Minimize Recursive Depth

The most common performance killer is excessive recursion. If a macro calls itself to process a list of items, each step adds overhead to the macro parser. Instead of deeply nested recursive structures, aim for flat or iterative expansions.

Consider a macro that generates a lookup table. Avoid this:

macro_rules! recursive_table {
    ($head:expr, $($tail:expr),+) => {
        // This forces N expansions for N items
        [ $head, recursive_table!($($tail),+) ]
    };
    ($last:expr) => { [$last] };
}

Instead, use repetition indicators (lesson 12) to expand in a single pass. The compiler handles the repetition internally much faster than the macro expansion engine handles recursion:

macro_rules! flat_table {
    ($($x:expr),*) => {
        // Single expansion pass: O(1) in the macro engine
        [$($x),*]
    };
}

Reduce Token Bloat

Each token you generate must be tracked, checked for hygiene, and type-checked later. To optimize:

  1. Avoid unnecessary macro_rules! re-definitions: If a helper macro is needed, define it once in a module rather than creating it inside the body of another macro, which forces the compiler to re-process the helper's definition on every invocation.
  2. Be specific with fragments: Using :tt (token tree) captures everything and forces the compiler to parse more than necessary. Use precise fragment specifiers like :expr or :ident (lesson 10) to limit the compiler’s search space.
  3. Limit internal intermediate code: Generate only what is strictly necessary. If you can move logic from a macro into a const fn, do so; const fn is cached and optimized by the compiler, whereas macros are expanded and re-parsed repeatedly.

Key Takeaway: Optimize macro performance by favoring internal repetition indicators over recursive expansion and moving logic into const fn to reduce the total volume of generated code.