We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Macros, FFI and Type-Driven Design step 7 of 28
Counting repetitions without ${count} (which is still unstable)
Count how many things a repetition matched — using only what actually works on stable Rust.
pub const N: usize = count_args!(a, b, c, d, e, f, g);
pub fn sizes() -> (usize, usize, usize, usize, [u8; N])
The fifth element of that tuple is a fixed-size array whose length came out of
the macro. That is not decoration: an array length is a const-evaluated
usize, so if the count works there, it really is computed at compile time and
costs nothing at run time.
Start by unlearning something
Search for “rust macro count repetitions” and you will be told, confidently and
in several places, that ${count($x)} was stabilised in Rust 1.80. The starter
uses it, so you can see what happens:
error[E0658]: meta-variable expressions are unstable
|
| ${count($x)}
| ^^^^^^^^^^^^
= note: see issue #83527 for more information
It is not stable. Not ${count}`, not `${index}, not ${len}`, not
`${ignore}. On rustc 1.95 every one of them is E0658. ${concat}` is tracked
separately (issue #124225) and is also unstable — which matters later, because
"generate a new identifier by pasting two together" is the single most requested
thing `macro_rules!` cannot do (18.16).
This is worth more than the counting trick itself. Macro material online is
unusually prone to describing nightly features as if they had landed, because the
tracking issues are long and the RFCs read as settled. When macro code from a
blog post does not compile, check E0658 before you check yourself.
## The two stable idioms
Both rest on the same trick: use a metavariable **purely for how many times it
repeats**, and throw its value away.
```rust
macro_rules! replace_expr {
($_ignored:tt $sub:expr) => { $sub }; } ```replace_expr!(anything 5)is5. The first fragment is captured and never transcribed. Now: ```rust // (1) — array length. The only one that can size an array. <[()]>::len(&[$(replace_expr!($x ())),*]) // (2) — addition chain. 0usize $(+ replace_expr!($x 1usize))* ``` Idiom (1) builds a temporary array of unit values — one()per matched fragment — and asks for its length.()is zero-sized, so the array occupies no memory, andlenon a fixed-size array is a compile-time constant. This is the one to use when the count has to be a const. Idiom (2) expands to0 + 1 + 1 + 1. Also const-evaluable, marginally simpler to read, and it will not size an array in every position. Note the shape of<[()]>::len(…). You cannot write[(); 3].len()here because you do not know the length — that is what you are computing.<[()]>::lennames the method on the *slice* type and passes a reference to the array, letting it coerce. ::: question The count is ausize, and[u8; N]needs ausize. So why does the description keep saying "an expression, not a literal"? Because some places in Rust's grammar need a *token* that is a literal, not an expression that evaluates to a number — and no amount of const-evaluation helps there. Array lengths andconstitems take a const *expression*, so both idioms work. But if you ever want to build an identifier, a string literal, or a#[repr(align(N))]attribute from a count, you are stuck: those positions want a literal token, andmacro_rules!has no arithmetic on tokens at all. It cannot add two token1s and get a token2. That is not a gap that will be closed by learning a better trick — it is a structural limit of a system that manipulates syntax without evaluating it, and it is one of the four or five things that push people to procedural macros (18.16). ::: ## Your job Replace both${count($x)}uses with a stable idiom, and implementreplace_expr!. The tuple must come out as: | element | value | | --- | --- | |count_args!()| 0 | |count_args!(solo)| 1 | |count_args!(a, …, g)| 7 | |count_args_add!(p, q, r,)| 3 | |[u8; N].len()| 7 | Use the array idiom forcount_args!— it is the one that has to size[u8; N]— and the addition idiom forcount_args_add!`, so you have written both.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.