Implementation Guide

Relevant source files

This document provides a detailed technical guide to the internal implementation of the tuple_for_each derive macro. It covers the macro processing pipeline, code generation strategies, and the architectural decisions behind the crate's design.

This guide focuses on how the derive macro transforms user code at compile time. For basic usage examples, see Getting Started. For API reference documentation, see API Reference.

Macro Processing Architecture

The tuple_for_each derive macro follows a standard procedural macro architecture with clear separation between parsing, validation, and code generation phases.

Processing Pipeline

flowchart TD
Input["TokenStream Input"]
Entry["tuple_for_each()"]
Parse["syn::parse_macro_input!()"]
Validate["Validation Logic"]
Valid["Valid Tuple Struct?"]
Error["Error::new_spanned()"]
Generate["impl_for_each()"]
NameConv["pascal_to_snake()"]
FieldIter["Field Iteration Logic"]
DocGen["gen_doc()"]
CodeGen["quote! Macro Generation"]
MacroNames["Macro Name Generation"]
ForEachGen["for_each Variants"]
EnumerateGen["enumerate Variants"]
Documentation["Inline Documentation"]
Methods["len() & is_empty() Methods"]
Macros["Generated macro_rules!"]
Output["proc_macro2::TokenStream"]
ErrorOutput["Compile Error"]

CodeGen --> Macros
CodeGen --> Methods
DocGen --> Documentation
Documentation --> CodeGen
Entry --> Parse
EnumerateGen --> CodeGen
Error --> ErrorOutput
FieldIter --> EnumerateGen
FieldIter --> ForEachGen
ForEachGen --> CodeGen
Generate --> CodeGen
Generate --> DocGen
Generate --> FieldIter
Generate --> NameConv
Input --> Entry
MacroNames --> CodeGen
Macros --> Output
Methods --> Output
NameConv --> MacroNames
Parse --> Validate
Valid --> Error
Valid --> Generate
Validate --> Valid

The pipeline starts with the tuple_for_each function as the entry point and flows through validation, name conversion, and code generation phases before producing the final token stream.

Sources: src/lib.rs(L10 - L24)  src/lib.rs(L58 - L122) 

Entry Point and Validation

The derive macro entry point performs essential validation to ensure the macro is only applied to tuple structs:

flowchart TD
TokenStream["TokenStream"]
ParseInput["syn::parse_macro_input!"]
AST["DeriveInput AST"]
CheckData["Check ast.data"]
IsStruct["Data::Struct?"]
CompileError["Compile Error"]
CheckFields["Check strct.fields"]
IsUnnamed["Fields::Unnamed?"]
CallImpl["impl_for_each()"]
ErrorToken["Error TokenStream"]
GeneratedCode["Generated TokenStream"]

AST --> CheckData
CallImpl --> GeneratedCode
CheckData --> IsStruct
CheckFields --> IsUnnamed
CompileError --> ErrorToken
IsStruct --> CheckFields
IsStruct --> CompileError
IsUnnamed --> CallImpl
IsUnnamed --> CompileError
ParseInput --> AST
TokenStream --> ParseInput

The validation logic specifically checks for Data::Struct with Fields::Unnamed, ensuring the derive macro only works on tuple structs like struct MyTuple(A, B, C).

Sources: src/lib.rs(L11 - L24) 

Code Generation Pipeline

The core code generation happens in the impl_for_each function, which orchestrates the creation of all generated functionality.

Generation Strategy

flowchart TD
subgraph subGraph4["Final Assembly"]
    ImplBlock["impl block"]
    MacroRules["macro_rules! definitions"]
    QuoteOutput["quote! expansion"]
end
subgraph Documentation["Documentation"]
    DocGeneration["gen_doc()"]
    ForEachDoc["macro_for_each_doc"]
    EnumerateDoc["macro_enumerate_doc"]
end
subgraph subGraph2["Code Vectors"]
    ForEachVec["for_each Vec"]
    ForEachMutVec["for_each_mut Vec"]
    EnumerateVec["enumerate Vec"]
    EnumerateMutVec["enumerate_mut Vec"]
end
subgraph subGraph1["Name Generation"]
    PascalToSnake["pascal_to_snake()"]
    MacroPrefix["macro_name"]
    ForEachMacro["macro_for_each"]
    EnumerateMacro["macro_enumerate"]
end
subgraph subGraph0["Input Processing"]
    ASTInput["DeriveInput & DataStruct"]
    StructName["ast.ident"]
    FieldCount["strct.fields.len()"]
end

ASTInput --> FieldCount
ASTInput --> StructName
DocGeneration --> EnumerateDoc
DocGeneration --> ForEachDoc
EnumerateDoc --> MacroRules
EnumerateMutVec --> MacroRules
EnumerateVec --> MacroRules
FieldCount --> EnumerateMutVec
FieldCount --> EnumerateVec
FieldCount --> ForEachMutVec
FieldCount --> ForEachVec
ForEachDoc --> MacroRules
ForEachMutVec --> ImplBlock
ForEachVec --> ImplBlock
ImplBlock --> QuoteOutput
MacroPrefix --> DocGeneration
MacroPrefix --> EnumerateMacro
MacroPrefix --> ForEachMacro
MacroRules --> QuoteOutput
PascalToSnake --> MacroPrefix
StructName --> DocGeneration
StructName --> PascalToSnake

The generation process creates separate code vectors for each variant (immutable/mutable × for_each/enumerate) and assembles them into the final token stream.

Sources: src/lib.rs(L58 - L122) 

Field Iteration Logic

The macro generates field access code by iterating over field indices and creating appropriate access patterns:

VariantAccess PatternGenerated Code Template
for_each&$tuple.#idx{ let $item = &$tuple.#idx; $code }
for_each_mut&mut $tuple.#idx{ let $item = &mut $tuple.#idx; $code }
enumerate&$tuple.#idxwith index{ let $idx = #idx; let $item = &$tuple.#idx; $code }
enumerate_mut&mut $tuple.#idxwith index{ let $idx = #idx; let $item = &mut $tuple.#idx; $code }

The field iteration uses syn::Index to generate numeric field accessors for tuple struct fields.

Sources: src/lib.rs(L64 - L83) 

Generated Code Structure

The macro produces a comprehensive set of functionality for each tuple struct, including both methods and macros.

Output Components

flowchart TD
subgraph Documentation["Documentation"]
    MacroDoc["Generated macro documentation"]
    Examples["Usage examples"]
end
subgraph subGraph2["Macro Variants"]
    ForEachImmut["($item:ident in $tuple:ident $code:block)"]
    ForEachMut["($item:ident in mut $tuple:ident $code:block)"]
    EnumImmut["(($idx:ident, $item:ident) in $tuple:ident $code:block)"]
    EnumMut["(($idx:ident, $item:ident) in mut $tuple:ident $code:block)"]
end
subgraph subGraph1["Generated Macros"]
    ImplBlock["impl StructName"]
    LenMethod["len() method"]
    IsEmptyMethod["is_empty() method"]
    ForEachMacro["struct_name_for_each!"]
    EnumerateMacro["struct_name_enumerate!"]
end
subgraph subGraph0["Generated Implementation"]
    ImplBlock["impl StructName"]
    LenMethod["len() method"]
    IsEmptyMethod["is_empty() method"]
end

EnumerateMacro --> EnumImmut
EnumerateMacro --> EnumMut
EnumerateMacro --> MacroDoc
ForEachMacro --> ForEachImmut
ForEachMacro --> ForEachMut
ForEachMacro --> MacroDoc
ImplBlock --> IsEmptyMethod
ImplBlock --> LenMethod
MacroDoc --> Examples

Each component serves a specific purpose in providing iteration capabilities for tuple structs.

Sources: src/lib.rs(L87 - L121) 

Method Generation

The implementation block includes utility methods that provide metadata about the tuple:

MethodReturn TypeImplementationPurpose
len()usize#field_num(const)Returns field count
is_empty()boolself.len() == 0Checks if tuple has fields

Both methods are const fn, allowing compile-time evaluation when possible.

Sources: src/lib.rs(L88 - L98) 

Helper Utilities

The implementation includes several utility functions that support the code generation process.

Name Conversion

The pascal_to_snake function converts Pascal case struct names to snake case for macro naming:

flowchart TD
Input["PascalCase String"]
Iterator["Character Iterator"]
Check["Check Character"]
IsUpper["is_ascii_uppercase()?"]
AddUnderscore["Add '_' if not empty"]
AddChar["Add character"]
ToLower["to_ascii_lowercase()"]
Result["snake_case String"]

AddChar --> ToLower
AddUnderscore --> ToLower
Check --> IsUpper
Input --> Iterator
IsUpper --> AddChar
IsUpper --> AddUnderscore
Iterator --> Check
ToLower --> Result

This conversion ensures that MyTupleStruct becomes my_tuple_struct_for_each! for the generated macro names.

Sources: src/lib.rs(L124 - L133) 

Documentation Generation

The gen_doc function creates inline documentation for generated macros:

Documentation TypeTemplateGenerated Content
"for_each"Field iterationUsage examples and description for*_for_each!
"enumerate"Indexed iterationUsage examples and description for*_enumerate!

The documentation includes proper cross-references to the derive macro and usage examples in ignore blocks.

Sources: src/lib.rs(L26 - L56) 

Token Stream Assembly

The final assembly uses the quote! macro to combine all generated components into a single proc_macro2::TokenStream. The macro leverages token interpolation to insert:

  • Field counts as literal numbers
  • Generated code vectors using #(#vector)* expansion
  • Dynamically created identifiers via format_ident!
  • Documentation strings through #doc attributes

Sources: src/lib.rs(L87 - L122)