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:- In a pattern: `$($item:expr),*` โ match zero or more comma-separated expressions, capturing each as `$item`
- In a template: `$($item)` or `$($item),` โ expand the template once for each captured item
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:
| Pattern | Meaning |
|---|---|
| `$($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
- Trait impl bulk generation โ `impl_from!`, `impl_display_for!`, `impl_error_from!` โ generate N identical impls from a list; used heavily in the standard library for numeric conversions.
- Struct/enum macro generation โ define a struct's fields as a comma-separated list and expand them in `struct`, `impl`, serialization, and migration code simultaneously.
- Configuration DSLs โ `routes! { GET / => handler, POST /api => api_handler }` โ repetition handles any number of entries, the macro generates all the routing code.
Key Differences
| Concept | OCaml | Rust |
|---|---|---|
| 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 blocks | PPX derivers โ separate plugin infrastructure | `macro_rules!` repetition โ built-in, no extra tooling |
| Trailing comma | Not applicable | `$(,)?` handles it โ idiomatic to support trailing commas |
| Nested repetition | Recursive list processing | Nested `$($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])