CodeBlanks Logo Icon CodeBlanks

Macro hygiene

Learn macro hygiene.

Macro hygiene

Rust macros are hygienic: local variables defined inside a macro don't mix with same-named variables in the calling code. Each identifier remembers the "context" it came from:

macro_rules! ikile {
    ($x:expr) => {{
        let gecici = $x;
        gecici * 2
    }};
}

let gecici = 5;
let s = ikile!(gecici + 1);   // the outer gecici is used: 12
println!("{s} {gecici}");    // 12 5

C preprocessor macros do text substitution and are open to such name clashes; Rust doesn't have this class of bugs.

Limits of hygiene

Hygiene applies to local variables and labels; not to functions, types or module paths. If a macro will be used in another module, paths must be written in full with $crate:::

#[macro_export]
macro_rules! hata {
    ($m:expr) => {
        $crate::kayit::yaz("HATA", $m)   // from the macro's crate
    };
}

A macro that intentionally wants to use one of the caller's variables must take the name as an argument ($ad:ident).

Practice Hygienic macro

Write the ikile! macro above. After defining let gecici = 5;, print the result of ikile!(gecici + 1) and the value of gecici separated by a space.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
macro_rules! ikile {
    ($x:expr) => {{
        let gecici = $x;
        gecici * 2
    }};
}

fn main() {
    let gecici = 5;
    let s = ikile!(gecici + 1);
    println!("{} {}", s, gecici);
}

Expected output

12 5

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What do the double braces <code>{{ .. }}</code> in the body do?

  • The outer pair delimits the macro body, the inner one opens a block expression Correct answer
  • It's an escape
  • It errors
  • It has no meaning

Explanation: The inner block lets the macro return several statements as a single expression.

2 Crate path

Question
#[macro_export]
macro_rules! hata {
    ($m:expr) => {
        ____::kayit::yaz("HATA", $m)
    };
}
Answer
#[macro_export]
macro_rules! hata {
    ($m:expr) => {
        $crate::kayit::yaz("HATA", $m)
    };
}

Explanation: $crate expands to the root of the crate where the macro is defined.

3 Concept and meaning

Term Meaning
Hijyen Local names don't mix
{{ .. }} Returns a block expression
$crate:: The macro's crate
#[macro_export] Exposes outside the crate

All lessons in Advanced