C.W.K.
Stream
Lesson 01 of 04 · published

Why Macros Exist

~10 min · macros, why, vs-functions

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

You've used macros since lesson one — println!, vec!, format! all end in !. This track demystifies what that ! means and when you'd write your own.

Macros are code that writes code

A macro runs at compile time and generates Rust source before the real compilation happens. That's fundamentally different from a function, which runs at runtime on values. A macro operates on the syntax itself — it sees tokens and produces tokens. The ! is how you tell a macro invocation from a function call.

What macros can do that functions can't

Three things. Variadic arguments: println! takes any number of arguments — a function can't. Arbitrary syntax: vec![1, 2, 3] and vec![0; 100] use syntax no function signature could express. Compile-time generation: #[derive(Debug)] writes an entire trait impl for you. Functions are runtime values; macros are compile-time code generators.

Functions abstract over values; macros abstract over syntax. When you need to vary the number of arguments, accept arbitrary syntax, or generate code at compile time, you need a macro. For everything else — and that's most things — a function is simpler, clearer, and easier to debug.

Use them sparingly

Macros are powerful but harder to read, write, and debug than functions. The Rust community's guidance is clear: reach for a function first, and only write a macro when a function genuinely can't express what you need. Most code you write will have zero custom macros — and that's correct.

Code

Macros you already use, and why they can't be functions·rust
fn main() {
    // These are all macros — note the !
    let v = vec![1, 2, 3];          // variadic + special syntax
    let v2 = vec![0; 5];            // repeat syntax: [0, 0, 0, 0, 0]
    println!("{} items", v.len());  // variadic, compile-time-checked format
    let s = format!("{v2:?}");      // builds a String
    assert_eq!(v.len(), 3);         // expands to a runtime check
    println!("{s}");
}

External links

Exercise

List three macros you've used in this quest and, for each, explain in one sentence why it has to be a macro rather than a function (variadic args? special syntax? compile-time code gen?). Then name one thing you wrote as a function that could NOT be a macro and explain why a function was right.
Hint
println! is variadic + format-checked; vec![0; n] uses repeat syntax; derive generates code. A plain computation on known values (like fn add(a, b)) is exactly what functions are for — no syntax magic needed.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.