Course contentsShow
← Rust
Lesson 1534 of 1,91013. Metaprogramming with MacrosFree lesson

Defining Attribute-Like Macros

You will build custom attribute macros to annotate and modify the behavior of struct fields.

In this lesson, you’ll learn how to extend the Rust compiler by defining custom attribute-like macros to annotate and modify the behavior of struct fields at compile time.

What are Attribute-Like Macros?

Unlike macro_rules! which performs pattern-based text substitution, an attribute-like macro is a procedural macro that acts as a gatekeeper for your code. When you place an attribute like #[my_custom_attr] above a struct or field, the compiler pauses, hands your code block to your procedural macro function, and waits for you to return a modified version of that code.

Modifying Struct Fields

While custom attributes are often applied to entire items, they are incredibly powerful when used to mark specific fields for code generation or metadata injection. To create one, you define a function marked with #[proc_macro_attribute]. This function receives two TokenStream objects: the attribute's arguments and the item being annotated.

Consider a scenario where you want to automate the validation of struct fields:

#[proc_macro_attribute]
pub fn validate(args: TokenStream, input: TokenStream) -> TokenStream {
    // 1. Parse the input into a struct definition
    // 2. Identify the field marked with #[validate]
    // 3. Inject validation logic (e.g., bounds checking)
    // 4. Return the modified TokenStream
}

struct User {
    #[validate(min = 18)]
    age: u8,
}

How it Works

When the compiler encounters #[validate], it executes your procedural macro. Because you receive the TokenStream of the annotated item, you can parse the field's type, name, and current attributes. By returning a new TokenStream, you can wrap the field access in a function, generate a "setter" method that enforces your constraints, or even hide the original field and replace it with a getter/setter pair.

The magic here lies in manipulation: your macro doesn't just "see" the code; it is the code generator. By analyzing the TokenStream, you can selectively modify fields, ensuring that boilerplate validation or serialization logic is injected automatically without the developer writing it by hand.

Key Takeaway: Attribute-like macros allow you to intercept struct declarations during compilation, providing a mechanism to inspect, modify, or augment individual fields based on custom metadata.