๐Ÿฆ€ Functional Rust

412: Macro Repetition Patterns

Difficulty: 3 Level: Advanced Generate repeated code from a variable number of inputs using `$(...)*` โ€” the macro equivalent of a loop.

The Problem This Solves

Some boilerplate follows a strict pattern: implement `From<i8>` for `f64`, `From<i16>` for `f64`, `From<u8>` for `f64`, ... โ€” ten identical implementations differing only in the source type. Or generate a struct with N fields from a list. Or implement a trait for every variant in a list. Writing these by hand is tedious and error-prone. Missing one type silently breaks code. Adding a new case means hunting through the file. Repetition patterns in `macro_rules!` solve this: list your inputs once, and the macro generates all the code. This is how the standard library implements arithmetic and conversion traits for all numeric types. A handful of macro invocations replace hundreds of identical `impl` blocks.

The Intuition

Repetition syntax in `macro_rules!` works like this: The separator (`,` in `$(...),`) appears between* repetitions. The trailing comma problem (common in Rust) is handled with `$(,)?` โ€” an optional trailing separator. Repetition can be nested: `$($field:ident: $ty:ty),*` captures field-type pairs. Both `$field` and `$ty` then repeat together in the template.

How It Works in Rust

// Sum of any number of values โ€” repetition in template
macro_rules! sum {
 () => { 0 };
 ($first:expr $(, $rest:expr)*) => {
     $first $(+ $rest)*   // expands: a + b + c + d
 };
}

// Generate multiple From impls in one macro call
macro_rules! impl_from_int {
 ($target:ty : $($source:ty),*) => {
     $(  // repeat once per source type
         impl From<$source> for $target {
             fn from(x: $source) -> $target {
                 x as $target
             }
         }
     )*
 };
}

// One line replaces 6 identical impl blocks:
impl_from_int!(f64 : i8, i16, i32, u8, u16, u32);

// Generate a struct with arbitrary named fields
macro_rules! make_struct {
 ($name:ident { $($field:ident : $ty:ty),* $(,)? }) => {
     #[derive(Debug, Default)]
     struct $name {
         $($field: $ty,)*   // expand each field
     }
 };
}

make_struct!(Config {
 port: u16,
 timeout: u32,
 debug: bool,
});

// Print a key-value table โ€” expand arbitrary number of pairs
macro_rules! print_table {
 ($($key:expr => $val:expr),* $(,)?) => {
     $(println!("  {:20} = {}", $key, $val);)*
 };
}

fn main() {
 println!("sum(1,2,3,4,5) = {}", sum!(1, 2, 3, 4, 5));
 println!("sum() = {}", sum!());

 let c = Config { port: 8080, timeout: 30, debug: true };
 println!("{:?}", c);

 let x: f64 = 42u32.into();  // From impl generated by the macro
 println!("u32 -> f64: {}", x);

 print_table!(
     "host" => "localhost",
     "port" => 8080,
     "debug" => true,
 );
}
Repetition separators:
PatternMeaning
`$($x:expr)`Zero or more, no separator
`$($x:expr),`Zero or more, comma-separated
`$($x:expr),+`One or more, comma-separated
`$($x:expr),* $(,)?`Zero or more, optional trailing comma

What This Unlocks

Key Differences

ConceptOCamlRust
Variadic operations`List.fold_left (+) 0 [1;2;3]` โ€” list at runtime`sum!(1, 2, 3)` โ€” expanded at compile time to `1 + 2 + 3`
Generating impl blocksPPX derivers โ€” separate plugin infrastructure`macro_rules!` repetition โ€” built-in, no extra tooling
Trailing commaNot applicable`$(,)?` handles it โ€” idiomatic to support trailing commas
Nested repetitionRecursive list processingNested `$($outer $(, $inner)*)` โ€” captures pairs, triples, etc.
// Repetition patterns in macros ($(...),*)

// sum of any number of values
macro_rules! sum {
    () => { 0 };
    ($first:expr $(, $rest:expr)*) => {
        $first $(+ $rest)*
    };
}

// product
macro_rules! product {
    () => { 1 };
    ($first:expr $(, $rest:expr)*) => {
        $first $(* $rest)*
    };
}

// create a struct with named fields from macro
macro_rules! make_struct {
    ($name:ident { $($field:ident : $ty:ty),* $(,)? }) => {
        #[derive(Debug, Default)]
        struct $name {
            $($field: $ty,)*
        }
    };
}

// implement multiple From impls at once
macro_rules! impl_from_int {
    ($target:ty : $($source:ty),*) => {
        $(
            impl From<$source> for $target {
                fn from(x: $source) -> $target {
                    x as $target
                }
            }
        )*
    };
}

impl_from_int!(f64 : i8, i16, i32, u8, u16, u32);

// print table with repetition
macro_rules! print_table {
    ($($key:expr => $val:expr),* $(,)?) => {
        {
            $(println!("  {:20} = {}", $key, $val);)*
        }
    };
}

make_struct!(Config {
    port: u16,
    timeout: u32,
    debug: bool,
});

fn main() {
    println!("sum(1,2,3,4,5) = {}", sum!(1, 2, 3, 4, 5));
    println!("sum() = {}", sum!());
    println!("product(1,2,3,4) = {}", product!(1, 2, 3, 4));

    let c = Config { port: 8080, timeout: 30, debug: true };
    println!("Config: {:?}", c);

    // From impls generated by repetition
    let x: f64 = 42u32.into();
    println!("u32 -> f64: {}", x);

    print_table!(
        "host" => "localhost",
        "port" => 8080,
        "debug" => true,
    );
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_sum_macro() {
        assert_eq!(sum!(1, 2, 3), 6);
        assert_eq!(sum!(), 0);
        assert_eq!(sum!(10), 10);
    }

    #[test]
    fn test_product_macro() {
        assert_eq!(product!(2, 3, 4), 24);
        assert_eq!(product!(5), 5);
    }
}
(* Repetition patterns in OCaml โ€” variadic via lists *)

(* Simulate variadic with list arguments *)
let sum_list xs = List.fold_left (+) 0 xs
let product_list xs = List.fold_left ( * ) 1 xs
let all_gt n xs = List.for_all (fun x -> x > n) xs

(* Variadic-like tuple construction *)
let zip3 a b c = List.map2 (fun (x,y) z -> (x,y,z)) (List.combine a b) c

let () =
  Printf.printf "sum [1;2;3;4;5] = %d\n" (sum_list [1;2;3;4;5]);
  Printf.printf "product [1;2;3;4] = %d\n" (product_list [1;2;3;4]);
  Printf.printf "all > 0: %b\n" (all_gt 0 [1;2;3])