We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Macros, FFI and Type-Driven Design step 2 of 28
Your first macro_rules!: matchers and transcribers
Add three rules to describe!: two literal-token cases and a final
catch-all. Keep the existing empty-input rule and the supplied run function.
pub fn run(n: usize) -> String
The macro must expand to these string literals:
| Tokens inside the invocation | Expansion |
|---|---|
| No tokens | "nothing" |
one |
"one thing" |
one, two |
"two things" |
| Any other valid token-tree sequence | "something else" |
Match those specific cases exactly. For example, one,, one two and
one, two, use the catch-all. Whitespace between tokens does not change a
match. The words one and two are literal tokens here, not variable values.
What run returns
The function contains seven macro invocations. They all need to expand
successfully during compilation, even though only one match arm runs for a
given n.
| n | Returned string |
|---|---|
0 |
nothing |
1 or 4 |
one thing |
2 or 5 |
two things |
3 or any value greater than 5 |
something else |
The quotes in the first table are Rust string-literal syntax, not characters in the returned string. The starter only handles empty input, so its other invocations fail to compile.
Matchers and transcribers
Each rule has a matcher before => and a transcriber after it. The matcher
recognizes the input tokens; the transcriber supplies the expansion. A macro
invocation is expanded by the compiler, rather than calling a runtime function.
The resulting expression can still evaluate to a value at runtime.
Here is a complete example with a different vocabulary:
macro_rules! reply {
(hello) => { "hi" };
($($other:tt)*) => { "unrecognized" };
}
fn main() {
assert_eq!(reply!(hello), "hi");
assert_eq!(reply!(goodbye), "unrecognized");
}
No variable named hello or goodbye is needed. The matcher recognizes or
captures those tokens, and the transcriber does not emit them as variable uses.
Macro-generated code is still subject to the relevant syntax, name-resolution,
type and borrow checks. Expansion interacts with name resolution; it is not
simply one text-replacement pass before all other compiler work.
Put the catch-all last
Rules are tried in source order. Rust uses the first successful match, without ranking rules by specificity. If its expansion is invalid, Rust reports that error; it does not try a later rule as a fallback.
$($rest:tt)* captures zero or more token trees. A token tree is one token or a
balanced delimited group. This pattern accepts the empty case and all the
other well-formed inputs in this exercise. Put it before the specific cases
and it will shadow them. Such ordering can compile while producing the wrong
answers, which is why the examples test more than the catch-all case.
Outer delimiters and inner groups
For matching, the invocation's outer delimiters can be parentheses, brackets
or braces. describe!(one), describe![one] and describe!{one} all select
the same rule.
Delimiters inside the invocation are still tokens to match. describe!((one))
contains a parenthesized group, so it does not match the literal (one) rule;
it reaches the catch-all. The first parentheses in a rule enclose the matcher,
while the extra parentheses in that invocation are part of its input.
The surrounding Rust syntax also matters. For example, brace-delimited macro invocations used as statements can omit a trailing semicolon. The supplied calls are expressions in match arms, so use them as written.
Keep the definition before run
This local, unqualified macro call uses textual scope. Keep macro_rules! describe before run, so the definition is in scope where the calls appear.
Rust macros also have path-based scoping and export mechanisms; this exercise
does not need them.
Your solution must compile, return the required strings 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.