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.