Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A derive macro is a type of procedural macro in Rust that automatically generates and appends code to structs, enums, or unions at compile time. Unlike attribute macros, derive macros do not modify or consume the original item; they strictly append new Abstract Syntax Tree (AST) nodes to the module scope alongside the annotated item.

Invocation Syntax

Derive macros are invoked using the #[derive(...)] attribute placed directly above a data structure declaration.
#[derive(CustomTrait)]
struct DataNode {
    id: u32,
    payload: String,
}

Architectural Mechanics

When the Rust compiler encounters a #[derive(...)] attribute, it executes the corresponding procedural macro function. The compilation pipeline follows these steps:
  1. Tokenization: The compiler converts the annotated data structure into a proc_macro::TokenStream.
  2. Parsing: The macro parses the TokenStream into an AST (usually leveraging the syn crate’s DeriveInput type).
  3. Transformation: The macro inspects the AST (e.g., extracting the struct’s identifier, generics, and fields) and constructs new Rust code (usually leveraging the quote crate).
  4. Expansion: The macro returns a new TokenStream containing the generated code. The compiler appends this generated TokenStream to the module’s AST in memory during the compilation process; it does not modify or append to the physical source file on disk.

Implementation Structure

Derive macros must be defined in a dedicated crate with the proc-macro = true directive in its Cargo.toml. The macro itself is a public function annotated with #[proc_macro_derive(Name)].
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(CustomTrait)]
pub fn custom_trait_derive(input: TokenStream) -> TokenStream {
    // 1. Parse the input TokenStream into a syn::DeriveInput AST node
    let ast = parse_macro_input!(input as DeriveInput);

    // 2. Extract the identifier (name) of the struct/enum
    let name = &ast.ident;

    // 3. Generate the new TokenStream using the quote! macro
    let expanded = quote! {
        impl CustomTrait for #name {
            fn execute(&self) {
                // Generated implementation logic
            }
        }
    };

    // 4. Convert the proc_macro2::TokenStream back to proc_macro::TokenStream
    TokenStream::from(expanded)
}

Helper Attributes

Derive macros can declare inert helper attributes. These attributes are scoped exclusively to the item being derived and are used to pass field-level or variant-level metadata to the macro during the parsing phase. Helper attributes are declared in the macro definition using the attributes(...) argument:
#[proc_macro_derive(CustomTrait, attributes(custom_helper))]
pub fn custom_trait_derive(input: TokenStream) -> TokenStream {
    // Implementation...
}
During invocation, the helper attribute can be applied to fields or variants. Because these helper attributes are inert, they do not trigger macro expansion themselves. After all derive macros applied to the item have been expanded, the macro expansion infrastructure explicitly strips these helper attributes from the syntax tree. The compiler does not simply ignore unknown attributes; if the macro expansion infrastructure did not strip these helper attributes, the compiler would reject them with an “unknown attribute” error.
#[derive(CustomTrait)]
struct DataNode {
    #[custom_helper(ignore)]
    id: u32,
    payload: String,
}

Technical Constraints

  • Target Restriction: Derive macros can only be applied to struct, enum, and union declarations. They cannot be applied to functions, modules, or trait definitions.
  • Additive Only: The output TokenStream is appended to the module’s AST. A derive macro cannot mutate, remove, or replace the original data structure definition.
  • Crate Isolation: The procedural macro must reside in a separate crate from the code that consumes it due to the compiler needing to compile the macro as a compiler plugin before it can parse the consuming crate’s AST.
Master Rust with Deep Grasping Methodology!Learn More