Hygiene¶
Level: 301 · deep dive
One line: A let written inside a macro can neither be seen nor clobbered by the code that calls it — local variables and labels resolve where the macro was defined — while functions, types and crate:: paths resolve where it was called, which is why an exported macro writes $crate::.
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¶
- The refusal: a macro that expands to
let x = 1;, then the caller readsx—E0425, cannot find valuexin this scope, with the 1.98.0 help line an identifier with the same name is defined here, but is not accessible due to macro hygiene - The protection it buys: a rule
{ let tmp = $e; tmp * tmp }called assquare!(tmp + 1)with the caller'stmp = 3gives 16 and leaves the caller'stmpat 3. std'sdbg!relies on the same thing — it binds its argument to a local namedtmp - Handing a name in on purpose: an
$name:identfragment carries the caller's identifier, somake_named!(z)does create azthe caller can use - Mixed-site hygiene as the Reference defines it: loop labels, block labels and local variables are looked up at the definition site, everything else at the invocation site — so a macro that calls
helper()calls whicheverhelperis in scope where it is used $crate: the path to the crate that defined the macro, for helpers the calling crate never imported — and the Reference's warning that$cratedoes not bypass privacy, so the helper still has to be visible from the call sitecrate::inside a macro means the calling crate, which is whatclippy::crate_in_macro_def(suspicious, warn by default) catches in an exported macro- Seeing it rather than inferring it:
-Zunpretty=expanded,hygieneprints a syntax-context number on every identifier — see Expanding a macro - What procedural macros get instead of one fixed rule: a span chosen per token — absolute paths and hygiene in the procedural macros section
The trap it exists for¶
Assuming hygiene covers every name. It covers locals and labels only. A macro that calls validate(&$x) calls the caller's validate if there is one, and an exported macro that writes crate::DEFAULTS passes the unit tests of the crate that defines it — where the calling crate and the defining crate are the same — and fails to compile in the first other crate that calls it.
Where this sits¶
Scope is about names, not values covers the ordinary scoping rules that hygiene bends. Exporting a macro covers where the macro's own name is visible; this page covers only the names inside its body.
See also¶
- Scope is about names, not values — the rules a macro body does not follow
- Exporting a macro —
#[macro_export], and why$cratematters once you use it - Expanding a macro — the printout that erases hygiene, and the flag that keeps it
- What
dbg!does — a std macro whose internaltmpnever meets yours - Macros by example: hygiene ↗ — the definition-site and invocation-site rule, and
$crate - The Little Book of Rust Macros: hygiene ↗ — a longer walk through the same rules
If you are coming from another language¶
- C. The preprocessor has no hygiene. With
#define SWAP(a, b) { int tmp = a; a = b; b = tmp; }, the callSWAP(tmp, x)declares a secondtmpthat hides the caller's; built withccon macOS and warnings silenced it compiles, runs and prints1 2— nothing swapped. The fix in C is a naming convention; in Rust the compiler keeps the twotmps apart. - C++. The same preprocessor, and the same bug. The standard advice is to replace such a macro with an inline function or a template, whose locals are ordinary scoped variables — which is the guarantee Rust gives the macro itself.
Po polsku¶
Higiena makr (macro hygiene) oznacza, że zmienna lokalna utworzona wewnątrz makra nie widzi zmiennych wywołującego i nie może ich przypadkiem nadpisać — tmp w makrze i tmp w twoim kodzie to dla kompilatora dwie różne nazwy. Higiena w macro_rules! jest jednak mieszana (mixed-site): dotyczy tylko zmiennych lokalnych i etykiet, a nazwy funkcji, typów i ścieżka crate:: są rozwiązywane w miejscu wywołania. Dlatego eksportowane makro odwołuje się do własnych pomocników przez $crate::.
Szukaj po polsku: higiena makr · zmienne lokalne w makrze · rust macro hygiene · rust $crate macro · mixed site hygiene