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
Count macro arguments with stable Rust
Implement two macros that count comma-separated token trees. One should use a unit array and the other an addition expression. Both must work in const contexts on stable Rust.
Keep the supplied public items:
pub const N: usize = count_args!(a, b, c, d, e, f, g);
pub fn sizes() -> (usize, usize, usize, usize, [u8; N])
Required result
Complete replace_expr!, count_args! and count_args_add!. Preserve the
supplied N and sizes definitions. The returned tuple must contain:
| Element | Value |
|---|---|
count_args!() |
0 |
count_args!(solo) |
1 |
count_args!(a, b, c, d, e, f, g) |
7 |
count_args_add!(p, q, r,) |
3 |
| The final array | Seven zero bytes. |
Each argument matches one tt token tree. An identifier such as a counts
once, as does a delimited group such as (a + b) or [1, 2]. The identifiers
do not need to name variables: the macros count syntax without evaluating it.
An ungrouped multi-token expression such as a + b does not match one tt.
Both macros must accept an empty invocation and an optional trailing comma after a nonempty list. A comma-only invocation is outside the exercise's input syntax; the supplied matcher reports a local ambiguity for it.
The harness selects tuple values with zero, one, seven and three.
array_len checks the final array's length and array_sum checks that its
bytes sum to zero. Unknown selectors return usize::MAX in the supplied harness.
Those output checks do not independently prove which macro technique you used.
Replace matched syntax with a fixed expression
replace_expr! receives one token tree followed by an expression. Emit only
the expression. This lets an outer repetition use the number of matches
without using their contents.
Here is a complete example of that helper:
macro_rules! replace_expr {
($_ignored:tt $sub:expr) => { $sub };
}
fn main() {
let value = replace_expr!((not_a_variable + either) 5usize);
assert_eq!(value, 5);
}
The ignored group is never evaluated or resolved as an expression. The
replacement can be unit () for the first counting macro, or 1usize
for the second.
Two const-compatible techniques
For count_args, repeat a replacement unit value inside an array, then take
its length with <[()]>::len(&[...]). Here [()] names the slice type, and
the borrowed array coerces to a slice. Specifying the element type also makes
the empty-array case unambiguous.
Unit has size zero, so the array's elements need no storage. That fact is separate from const evaluation: a const context requires the count to be evaluated at compile time.
For count_args_add, start with 0usize and repeat + 1usize, using
replace_expr for each matched argument. The zero makes an empty invocation
a valid expression. Use repetition rather than recursion for this exercise.
Both techniques can size an array. The addition expression is just as valid in an array-length const context as the slice-length expression. Implement one macro with each technique to practice both, not because one lacks that capability. Very large expansions can still hit compiler limits; this exercise uses small argument lists.
Why the starter fails on stable Rust
The starter uses ${count($x)}, a macro metavariable expression. Rust 1.98.1
rejects it with E0658 because that feature is unstable. Replace both uses with
the stable techniques above; do not enable a nightly feature gate.
A computed const expression is not automatically a literal token. Array
lengths accept const expressions, while an attribute such as repr(align(8))
requires its numeric argument in the attribute's accepted syntax. Computing
an equivalent const does not make repr(align(N)) valid. Macro rules can map
known tokens to literal tokens, but ordinary const evaluation does not rewrite
tokens for an attribute.
Submissions must compile, pass the output checks and pass Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.