Under The Hood
backend rust Rust 1.x

Macro System: macro_rules! AST Matching vs Procedural Derive Macros (syn/quote)

Last updated
Prerequisites:
Rust syntax, traits, and derives
Understanding of ASTs and metaprogramming
  • rust
  • macros
  • macro-rules
  • procedural-macros
  • syn
  • quote

Read at your depth

The practical view

Rust has two macro systems. macro_rules! is declarative: you define patterns with matchers ($x:expr, $y:ty) that expand to token templates. Procedural macros are functions that process TokenStreams: #[derive(...)] (derive macros), attribute macros (#[test]), and function-like macros (println!). The syn crate parses a TokenStream into an AST, quote generates code with #var interpolation. Common derives (#[derive(Debug, Clone, PartialEq)]) are procedural macros in the std/derive tooling. Write a derive: create a proc-macro crate, #[proc_macro_derive(MyTrait)] pub fn derive(input: TokenStream) -> TokenStream.

The same idea in other frameworks

react equivalent
N/A — Rust language feature.

Legacy vs modern

macro_rules! template vs procedural derive

Declarative macros substitute patterns; procedural macros compute on the AST — e.g. implementing a trait by inspecting struct fields.

before → after
Hand-writing trait impls (or macro_rules! for simple repetition)
impl fmt::Debug for Point { ... } // ❌ OLD: boilerplate per struct
Derive macro (procedural)
#[derive(Debug, Clone, PartialEq, Eq)] // ✅ NEW: generated impls

Interview gotchas

Context

This tests the macro taxonomy — interviewers want the pattern-matching vs AST-computing distinction and the practical guidance for when procedural power is needed.

The mechanical answer

macro_rules! is declarative and shape-based: you write match patterns ($x:expr) and expansion templates; the compiler substitutes token fragments. It's great for small syntax extensions, repeated patterns (vec!, println!), and readability — but it cannot inspect the semantic structure (fields, generics) or do computation on the AST; it's pure token-tree substitution with limited recursion. Procedural macros are full functions over TokenStream: they parse the input with syn into a rich AST (struct fields, generics, attributes), compute arbitrarily, and emit new code with quote. Choose macro_rules! for simple, syntactic sugar; choose procedural macros when you need to read the item's structure (derive implementations, attribute transformations like serde's #[serde(rename)], or building complex DSLs). The cost of procedural macros is compile time and complexity (syn/quote, proc-macro crates).

Trap

The naive answer is 'procedural macros are just more powerful macro_rules'. They operate on a different level (AST computation vs token substitution) and live in separate proc-macro crates. Another trap: thinking macro_rules! can inspect fields — it cannot; only procedural macros parse structure. Candidates who contrast the two expansion models show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.