TL;DR:
macro_rules!defines declarative (pattern-matching) macros in Rust. You define a set of rules: each rule matches a pattern of tokens and expands to replacement code. They are used for reducing boilerplate, creating DSLs, and writing variadic functions (functions with variable numbers of arguments).vec![1, 2, 3],println!, andassert!are allmacro_rules!macros. They compile faster and are simpler than proc-macros, but cannot inspect types or generate complex impls.
How Do You Define a macro_rules! Macro?
A macro rule has a pattern (matcher) and an expansion (transcriber), separated by =>. Since Rust 2018 Edition, macro_rules! macros can be imported with use rather than relying on #[macro_use].
macro_rules! say_hello {
() => {
println!("Hello!");
};
($name:expr) => {
println!("Hello, {}!", $name);
};
}
say_hello!(); // Hello!
say_hello!("Rustacean"); // Hello, Rustacean!Each arm is tried in order. The first matching pattern wins. $name:expr is a metavariable that captures an expression.
What Are the Common Metavariable Types?
Metavariable designators specify what kind of syntax each capture accepts.
| Designator | Matches |
|---|---|
expr | Any expression |
ident | An identifier (variable/function name) |
ty | A type |
pat | A pattern |
stmt | A statement |
block | A { ... } block |
literal | A literal value |
tt | A single token tree (most flexible) |
Use tt when you need to match arbitrary syntax that doesn't fit a more specific designator.
How Do You Write Variadic Macros?
Use $(...)* or $(...)+ to repeat a pattern zero-or-more or one-or-more times.
macro_rules! sum {
($($x:expr),*) => {{
let mut total = 0;
$(total += $x;)*
total
}};
}
let result = sum!(1, 2, 3, 4, 5); // 15$($x:expr),* captures a comma-separated list of expressions into $x. The $(total += $x;)* expands one statement per captured value.
macro_rules! vs Proc-Macro in 2026
macro_rules! is pattern matching on tokens; proc-macros are full Rust programs that transform token streams; and in 2026 both remain essential, but for different jobs.
macro_rules! | Proc-macro | |
|---|---|---|
| Syntax | Pattern/template | Rust code with syn/quote |
| Type inspection | ❌ | ✅ |
| Separate crate required | ❌ | ✅ |
| Compile time | Fast | Slower |
| Maintainer | Rust compiler team (built-in) | Community (syn, quote) |
| Best for | Repetition, DSLs, variadic | Derive impls, complex codegen |
| Error messages | Confusing (expansion site) | Better with proc-macro-error |
If you need to reduce boilerplate for simple repetition or write a small DSL inside your crate, choose macro_rules!. If you need to inspect struct fields, generate trait implementations from attributes, or produce derives like #[derive(Serialize)], choose a proc-macro; the extra compile cost is worth the power.
Frequently Asked Questions
Add #[macro_export] above the macro_rules! definition. This places it at the crate root so consumers can use it with use my_crate::my_macro.
{{ }} inside a macro expansion creates a block expression. The outer braces are part of the macro syntax; the inner braces are the Rust block. Use it when your expansion needs to be a single expression.
Macro expansion happens before type-checking, so errors point to the expanded code rather than the macro call site. Use cargo expand (via cargo-expand) to see the generated code and debug.
Yes. A macro_rules! macro can call itself in its expansion. This is commonly used to peel off one element at a time from a list, enabling variadic behavior. Be careful about hitting the default recursion limit (128); increase it with #![recursion_limit = "256"] at the crate root if needed.
Yes; Rust's declarative macros are hygienic by default. Variables introduced inside a macro expansion do not leak into the caller's scope, preventing accidental name collisions. This is a key advantage over C preprocessor macros.
Sources
- Rust Book; Macros
- Rust Reference; macro_rules!
- The Little Book of Rust Macros
- cargo-expand on crates.io
Related Glossary Terms
- macro: Overview of all macro types in Rust
- proc-macro: Procedural macros for more complex code generation
- derive:
#[derive(...)]is powered by proc-macros - impl-trait: Return-position
impl Traitcan replace some macro patterns - generic: Generics and macros both reduce boilerplate; know when to use each
Keep Reading
- Learn Rust in 2026: Macros are a key part of idiomatic Rust code

