Procedural & Derive Macros
Macros let Rust generate code at compile time. Procedural macros take this furthest — running real Rust over your source's tokens to power tools like serde's derives and web-framework routing attributes.
Learn Procedural & Derive Macros in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll meet the three kinds of proc macros, set up a proc-macro crate, and write a working derive macro with syn and quote — contrasting them with declarative macro_rules!.
What You'll Learn in This Lesson
1️⃣ Declarative Macros First (macro_rules!)
Before the procedural kind, know the simpler macro_rules! macros: they match patterns and substitute code, need no extra crate, and are strongly hygienic. Reach for these first.
The $( ... )* repetition expands once per matched expression — pure pattern substitution, no arbitrary code.
2️⃣ A Procedural-Macro Crate
Procedural macros are compiler plugins, so they live in their own crate marked proc-macro = true . That crate depends on syn (to parse tokens) and quote (to generate them). There are three kinds: derive, attribute-like, and function-like.
A proc-macro crate exports macros, not ordinary runtime items, which is why libraries often pair a macro crate with a regular one that re-exports it.
3️⃣ Writing a Derive Macro
A derive macro takes a TokenStream , parses it with syn into a syntax tree, builds new code with quote! , and returns the result. The flow is always parse, then generate, then return.
Inside quote! , #name splices in the parsed type's identifier — the engine behind real-world derives like serde's.
Write a declarative macro_rules! that expands to the larger of two expressions.
📋 Quick Reference — Macros
Practice quiz
How many kinds of procedural macros does Rust have?
- Three
- Five
- One
- Two
Answer: Three. There are three: derive macros, attribute-like macros, and function-like macros.
Which macro kind powers #[derive(Debug)]-style code generation?
- An attribute macro
- A function-like macro
- A derive macro
- A declarative macro
Answer: A derive macro. Derive macros generate code from a #[derive(...)] applied to a struct or enum.
Where must procedural macros be defined?
- In a build.rs file
- In a special crate with proc-macro = true
- In any module
- Inside main.rs only
Answer: In a special crate with proc-macro = true. Proc macros live in their own crate marked proc-macro = true in Cargo.toml.
What type do procedural macro functions take and return?
- Vec<u8>
- syn::File
- String
- proc_macro::TokenStream
Answer: proc_macro::TokenStream. Proc macros operate on proc_macro::TokenStream values: the tokens in, the generated tokens out.
What is the syn crate used for?
- Parsing a TokenStream into a syntax tree
- Sending HTTP requests
- Running benchmarks
- Formatting output
Answer: Parsing a TokenStream into a syntax tree. syn parses raw tokens into a structured Rust syntax tree you can inspect.
What does the quote crate provide?
- A test runner
- A quote! macro to turn Rust-like templates back into tokens
- Async tasks
- A logging facade
Answer: A quote! macro to turn Rust-like templates back into tokens. quote! lets you write Rust code as a template and produce the corresponding TokenStream.
Which attribute marks a function as a derive macro implementation?
A derive macro function is annotated with #[proc_macro_derive(Name)].
What does macro hygiene help prevent?
- Memory leaks
- Data races
- Slow compilation
- Accidental capture or collision of identifiers from the macro and call site
Answer: Accidental capture or collision of identifiers from the macro and call site. Hygiene keeps identifiers introduced by a macro from clashing with names at the call site.
How do declarative macros differ from procedural ones?
- Declarative macros need a separate crate
- Proc macros can't generate code
- macro_rules! matches patterns; proc macros run arbitrary Rust on tokens
- They are identical
Answer: macro_rules! matches patterns; proc macros run arbitrary Rust on tokens. macro_rules! uses pattern-based substitution, while proc macros execute real Rust code over TokenStreams.
Which kind would you use for a custom #[route("/path")] attribute on a function?
- A derive macro
- An attribute-like macro
- A declarative macro
- A function-like macro
Answer: An attribute-like macro. Attribute-like macros define new outer attributes such as #[route(...)] that transform the item they annotate.