Macro Repetition
Macro repetition allows a macro_rules! pattern to match and expand variable-length sequences of tokens using the syntax $( ... ) sep rep_op, where sep is an optional separator token (such as , or ;) and rep_op is one of three repetition kleene operators: *, +, or ?.
Repetition Operators
| Operator | Matcher Syntax | Expansion Syntax | Meaning |
|---|---|---|---|
* (Zero or more) |
$($x:expr),* |
$($x),* |
Matches 0, 1, or more occurrences separated by , |
+ (One or more) |
$($x:expr),+ |
$($x),+ |
Matches 1 or more occurrences separated by , |
? (Zero or one) |
$(,)? or $($vis:vis)? |
$($x)? |
Optional element (no separator allowed before ?!) |
Supporting Optional Trailing Commas ($(,)?)
Appending $(,)? after a comma-separated repetition $($elem:expr),+ $(,)? allows callers to write either my_macro!(1, 2, 3) (no trailing comma) or multi-line my_macro!(1, 2, 3,) (with a trailing comma) inside a single rule!
Parallel Zipping and Nested Repetitions
When two different metavariables $keys and $vals are repeated at the same nesting depth in the matcher, expanding $($keys => $vals),* in the transcriber zips them together in lockstep (requiring both sequences to have the exact same length):
use std::collections::HashMap;
macro_rules! zip_map {
// Matches two parallel bracketed lists `[k1, k2, ...] => [v1, v2, ...]`
( [ $($key:expr),* $(,)? ] => [ $($val:expr),* $(,)? ] ) => {{
let mut map = HashMap::new();
$(
map.insert($key, $val);
)*
map
}};
}
fn main() {
let scores = zip_map!(
["alice", "bob", "carol",] => [95, 88, 92,]
);
println!("alice = {}, carol = {}", scores["alice"], scores["carol"]);
}
alice = 95, carol = 92
Metavariable Expressions in Expansions (${count(...)}, ${index()})
When generating array types [T; N] or indexed tuple fields from a repetition, counting how many elements matched $($elem:expr),* traditionally required a recursive helper macro or a slice-length trick <[_]>::len(&[ $( replace_expr!($elem, ()) ),* ]):
macro_rules! replace_with_unit {
($_t:tt) => {
()
};
}
macro_rules! fixed_array_with_len {
( $( $item:expr ),* $(,)? ) => {{
// Computes the exact compile-time count of `$item` repetitions:
const LEN: usize = <[()]>::len(&[ $( replace_with_unit!($item) ),* ]);
let arr: [_; LEN] = [ $( $item ),* ];
(LEN, arr)
}};
}
fn main() {
let (len, arr) = fixed_array_with_len![10, 20, 30, 40];
println!("compile-time len = {}, array = {:?}", len, arr);
}
compile-time len = 4, array = [10, 20, 30, 40]