We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Macros, FFI and Type-Driven Design step 12 of 28
Follow-set rules: why the compiler rejects your matcher
Repair four macro matchers so the supplied all() function compiles and
returns [3, 30, 1, 0, 1, 0, 6, 15].
pub fn all() -> Vec<i64>
Keep all(), its eight macro calls and the macro transcribers unchanged.
Change only the matchers, the patterns before => in each macro definition.
Calls in all() |
Required results |
|---|---|
add_pair!(1 + 2), add_pair!(10 + 20) |
3, 30 |
type_size!(u32 bytes 4), type_size!(u8 bytes 4) |
1, 0 |
matches_either!(2, 1 | 2), matches_either!(5, 1 | 2) |
1, 0 |
run_then!(let z = 3; then z * 2), run_then!(let q = 10; then q + 5) |
6, 15 |
type_size reports whether the byte size equals the supplied number.
matches_either reports whether the value matches either pattern. Both use
1 for true and 0 for false.
Why these definitions are rejected
A fragment such as $a:expr captures a Rust expression. Rust restricts which
tokens may follow certain fragment kinds in a matcher. These follow-set
rules leave room for future syntax without changing the meaning of existing
macros. A matcher can violate them even if its intended calls look clear.
The rules are checked at macro definition time. For example, $a:expr +
is rejected because + is not an allowed follower for an expression fragment.
The problem is in the matcher, before expansion of any particular call.
Repair each boundary
add_pair: The supplied operands are literals. Use a fragment kind that
matches those operands and permits the following +, such as literal.
This repairs these calls; it does not turn the macro into a parser for
arbitrary unparenthesized expressions.
type_size: $t:ty bytes is invalid because bytes cannot follow a
ty fragment. The supplied types u32 and u8 are single tokens, so tt
can capture them. One tt matches one token or one delimited token tree;
it does not automatically capture an ungrouped type such as Vec<u8>.
matches_either: In the edition used here, pat includes top-level
alternatives, so a literal | cannot follow $p:pat. Use pat_param to
capture an individual alternative before the separator. The macro definition's
edition controls this rule; before edition 2021, pat had the narrower form.
run_then: A stmt fragment cannot be followed directly by then, but
it can be followed by ;. For the supplied let statements, the fragment
does not consume the trailing semicolon. Match that semicolon explicitly
before then.
Do not add a semicolon after $s in the transcriber. The captured statement
is reinserted as a parsed statement there, and the supplied expansion already
works. An extra semicolon triggers redundant_semicolons for these calls.
The general rule has an exception: item statements that require semicolons,
such as use items, include them in a stmt fragment.
A legal expression separator
This complete example uses ; to separate two expression fragments:
macro_rules! add_values {
($left:expr; $right:expr) => {
$left + $right
};
}
fn main() {
let x = 4;
assert_eq!(add_values!(x * 2; 3), 11);
}
A comma or => can also follow an expr or stmt fragment. Other kinds
have their own rules: literal and tt have no follow-set restriction,
while vis does. An unrestricted follow set still does not guarantee that
a matcher is unambiguous or that its expansion is valid Rust.
Your solution must preserve the supplied calls, produce all eight results 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.