🎓 A1Learner
🔍

Rust Counting with Macros

Counting with macros means hiding a count inside the code a macro writes. A macro cannot count directly while it expands.

A helper that throws its input away

This helper takes any token plus a stand-in value, and yields only the stand-in:

macro_rules! replace_with_unit {
    ($_t:tt $sub:expr) => { $sub };
}

Turn tokens into units, then measure the array

One () per token goes into an array literal, and .len() does the counting:

($($t:tt)*) => {
    <[()]>::len(&[$(replace_with_unit!($t ()),)*])
};

The simple adding way — fine for a few tokens

Each token becomes a + 1. Short inputs only: the expression tree grows as deep as the input is long:

($($tts:tt)*) => { 0usize $(+ replace_with_unit!($tts 1usize))* };

Example

macro_rules! replace_with_unit {
    ($_t:tt $sub:expr) => { $sub };
}
macro_rules! how_many {
    ($($t:tt)*) => {
        <[()]>::len(&[$(replace_with_unit!($t ()),)*])
    };
}
fn main() {
    println!("words = {}", how_many!(one two three four));
    println!("none = {}", how_many!());
}

Key points

  • A macro cannot count directly
  • Expansion produces code
  • It does not run anything
  • So the count must be encoded in the generated code
  • Replacement trick: swap every captured token for the same fixed value, then measure how many copies there are
  • Slice-length trick: replace each token with a unit value ()
  • Build the array [(), (), ()] and read its length
  • The count becomes an ordinary .len() call
  • The adding trick, 0 + 1 + 1 + 1, also works for small inputs
  • It builds one deep expression tree
  • That tree can overwhelm the compiler on long inputs
  • Recursive counting — one call per token — hits the macro recursion limit quickly, so it suits only short inputs

In this example, how_many captures its whole input as tokens. The expansion swaps each token for the unit value (), using replace_with_unit. It places the results in an array. Four tokens become [(), (), (), ()]. The generated code then asks for that array's length, which is the count. main prints words = 4 for the four words. The second call passes nothing: the array is empty, its length is 0, and the program prints none = 0. The counting happened in the shape of the generated code — no counter ever existed.

Note

The slice-length version is the one real crates use

Its generated code stays shallow, so it keeps working for thousands of tokens

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

In the slice-length counting trick, what is actually counted?