Course contentsShow
← Rust
Lesson 1518 of 1,91013. Metaprogramming with MacrosFree lesson

Repetition Indicators and Delimiters

You will master the use of kleene operators to iterate over variable-length argument sequences.

In this lesson, you will master how to use Kleene operators and delimiters to process variable-length argument lists within your declarative macros.

When writing macro_rules!, you often need to handle sequences of tokens—such as function arguments or struct fields—without knowing in advance how many there will be. Rust provides three repetition indicators to handle this, which work in tandem with delimiters like commas or semicolons.

The Kleene Operators

To indicate repetition in your pattern matching, you use one of the following operators following a group enclosed in $():

  • *: Zero or more repetitions.
  • +: One or more repetitions.
  • ?: Zero or one repetition (rarely used in basic repetition patterns).

Using Delimiters

A repetition pattern is defined as $(... )separator indicator. The separator is the token that must appear between each instance of the repeated pattern. If you provide a comma, for example, the macro expects the tokens to be comma-separated.

Consider this example where we want to build a simple vec!-like macro:

macro_rules! register_list {
    ( $( $reg:ident ),* ) => {
        {
            let mut list = Vec::new();
            $(
                list.push(stringify!($reg));
            )*
            list
        }
    };
}

fn main() {
    let regs = register_list!(R0, R1, R2, R3);
    // Expands to: list.push("R0"); list.push("R1"); ...
}

How it Works

  1. The Pattern: $( $reg:ident ),* tells the compiler to look for an identifier ($reg), followed by a comma (,), and repeat this sequence zero or more times (*).
  2. The Expansion: The $( ... )* block on the right-hand side is also repeated. The compiler replaces each $reg with the specific identifier captured during the match phase.
  3. Delimiters: Note that the separator in the pattern (the comma) does not need to appear in the expansion. You can choose to use a semicolon or even nothing at all in the expansion block, regardless of what you used as a separator in the match pattern.

By nesting these indicators, you can define sophisticated DSLs that abstract away repetitive boilerplate in your hardware abstraction layer or system startup sequences.

Key Takeaway: Use the $(...)* or $(...)+ syntax combined with a separator token to map variable-length input sequences directly into expanded code.