Expanding a macro¶
Level: 301 · deep dive
One line: cargo expand, rustc -Zunpretty=expanded and trace_macros! show the code a macro wrote — all three rest on unstable compiler features — and the printout is a tree printed back as text, so it can read differently from the program that was compiled.
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The bullets below are the questions the finished page has to answer.
What it has to cover¶
RUSTC_BOOTSTRAP=1 rustc --edition 2024 -Zunpretty=expanded main.rs: the command Printing the HIR takes apart word by word, one stage earlier — macros are gone,forand?are still there, andprintln!stops atformat_args!, which is built into the compiler- The parentheses you did not write:
square!(1 + 2)with the rule$e * $eprints as(1 + 2) * (1 + 2), because a matched$e:expris one parsed node - The printout that lies about names: a macro
{ let tmp = 2; $e * tmp }called astimes_two!(tmp)with the caller'stmp = 3prints 6 on 1.98.0, and-Zunpretty=expandedshows{ let tmp = 2; tmp * tmp }— which, pasted back, prints 4 -Zunpretty=expanded,hygiene: every identifier annotated with a syntax context, so the twotmps print with different numbers after the#— the way to see Hygiene instead of guessing itcargo expand↗: its README describes it as a wrapper aroundcargo rustc --profile=check -- -Zunpretty=expandedthat runs rustfmt over the result — how does it get a-Zflag through a stable toolchain, and does it show#[derive]output too?trace_macros!(true): on stable 1.98.0 the call isE0658(issue #29598), yet thenote: trace_macrolines showing each expansion step are still printed; underRUSTC_BOOTSTRAP=1with#![feature(trace_macros)]it compiles. When is a step-by-step trace the only readable view — a recursivettmuncher?-Zmacro-backtrace, the flag everythis error originates in the macronote names: thein this expansion of …frames it adds, and when they help- Why none of these outputs compiles on stable as-is: the expanded file opens with
#![feature(prelude_import)]
The trap it exists for¶
Treating the expanded printout as the program. It is the macro's output with hygiene erased, so two variables that rustc kept apart print under one name, and the code you paste back to "test the expansion" computes something else. Use expanded,hygiene whenever the question is about which name refers to what.
Where this sits¶
Printing the HIR owns the command itself: RUSTC_BOOTSTRAP=1, the --edition trap, the table of -Zunpretty values and the promise-free output. This page is only the reading of a macro's expansion while you are writing the macro.
See also¶
- Printing the HIR — the same flag family, one stage later, and every trap of the command line
rustup default nightly— the other way to get-Zflags, and why it is the wrong default- Hygiene — what the
#numbers inexpanded,hygieneare telling you - Repetition — the depth errors that expansion helps you read
- Macros — where
cargo expandis first mentioned trace_macrosin the unstable book ↗ — the feature's entry
If you are coming from another language¶
- C and C++.
cc -Eprints the source after the preprocessor, and Printing the HIR showsTWICE(21)becoming((21) * 2). The difference is what you get back:-Eoutput is plain text that compiles as it stands, because the preprocessor had no idea of scope to erase; Rust's printout is a syntax tree turned back into text, missing exactly the hygiene information that made it correct. - Go. Generated code from
go generateis an ordinary file in the repository, read and reviewed like any other source. A Rust macro's expansion is never written anywhere unless you ask the compiler for it.
Po polsku¶
Rozwinięcie makra (macro expansion) da się zobaczyć na trzy sposoby — cargo expand, rustc -Zunpretty=expanded i trace_macros! — i wszystkie trzy opierają się na niestabilnych funkcjach kompilatora. Pułapka: wydruk to drzewo składni zamienione z powrotem w tekst, bez informacji o higienie, więc dwie różne zmienne tmp wyglądają na wydruku jak jedna, a kod skopiowany z wydruku liczy co innego niż oryginał. Kiedy pytanie brzmi „która nazwa do czego się odnosi”, użyj -Zunpretty=expanded,hygiene.
Szukaj po polsku: rozwinięcie makra · podgląd kodu po makrach · cargo expand · rustc unpretty expanded hygiene · rust trace_macros