Course contentsShow
← Rust
Lesson 154 of 1,9102. The Ownership ModelFree lesson

Ownership in Macro-Generated Code

Ensure that generated code respects borrow checker rules through robust macro design.

In this lesson, you will learn how to design macros that generate code which satisfies the Rust borrow checker.

When you write a macro, you aren't just writing text; you are generating code that the compiler must validate against its strict ownership and borrowing rules. A common pitfall is generating code that attempts to move a variable twice or keeps a reference to a variable that has already gone out of scope.

The "Hidden" Variable Trap

Consider a macro designed to print a resource. If the macro takes ownership of a variable and then tries to use it again, the generated code will fail to compile.

macro_rules! consume_and_log {
    ($val:expr) => {
        println!("Value is: {:?}", $val);
        drop($val); // The macro consumes the value
    };
}

let x = String::from("Hello");
consume_and_log!(x);
// println!("{}", x); // Error! x was moved into the macro's expansion.

To avoid borrow checker errors, your macro design must be intentional about how it handles parameters. If you want the generated code to leave the original variable accessible, you should pass a reference (&) to the macro instead of the value itself.

Ensuring Hygiene and Ownership

Because macros expand into the caller's scope, they can inadvertently conflict with existing variables or violate ownership invariants. To keep your generated code safe:

  1. Pass references when possible: If the macro only needs to read a value, define the macro to accept &$val.
  2. Use block scoping: Wrap the generated code in a block { ... } if you need to create temporary variables. This ensures that any temporary resources created by the macro are dropped immediately, preventing lifetime leaks.
  3. Avoid double-moves: If your macro must consume an input, document it clearly so the user knows the variable will no longer be available after the macro expansion.

When designing macros, always ask: "If I wrote this code manually by hand, would the compiler accept the ownership flow?" If the answer is no, your macro design needs adjustment.

Key Takeaway: Macros must be treated as "code templates" that are subject to the same strict ownership and borrowing rules as handwritten code; always define your macro arguments to reflect whether they should be borrowed or moved.