We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Macros, FFI and Type-Driven Design step 14 of 28
Build vector, pattern-matching and map macros
Finish three macro_rules! definitions, then use the supplied run function
to check them. my_vec! and my_matches! practice forms of the standard
vec! and matches! macros. hashmap! is a custom map literal macro;
the standard library does not provide a macro with that name.
pub fn run(xs: Vec<i64>) -> (Vec<i64>, Vec<bool>, Vec<(String, i64)>)
Keep the supplied driver and counting helpers. Complete the macro arms rather than replacing their uses with ordinary expressions.
Required forms
| Macro | Forms to support |
|---|---|
my_vec! |
Empty, a comma-separated list with an optional trailing comma, and my_vec![element; count]. |
my_matches! |
An expression and pattern, optionally followed by if guard. Top-level or-patterns must work. |
hashmap! |
Empty, or one or more key => value pairs with an optional trailing comma. |
These are exercise implementations, not exact copies of every standard-library
detail. In particular, this my_matches! contract does not require a trailing
comma after the pattern or guard.
What run returns
The tuple contains three collections:
nums:[1, 2, 3, 0, 0]followed by all input values.flags: four booleans about the first input value, using0for empty input. In order, they test equality to zero, membership in1..=9, negativity, and membership outside0..=9.pairs: sorted entries foralpha = 1,beta = 2,empty_len = 0andgamma = sum(xs). The input keeps intermediate sums withini64.
For xs = [-4, 10], the flags are [false, false, true, true] because all
four tests examine -4. The second input contributes to the sum, which is 6.
The final flag is an or-pattern, not another guard.
Build the vector forms
The empty arm already returns Vec::new(). Add $(,)? to the list matcher
to allow one trailing comma. Its existing expansion is:
<[_]>::into_vec(::std::boxed::Box::new([$($x),+]))
This macro fragment constructs an array from the expressions and converts its boxed slice into a vector. The conversion reuses the box's allocation. Do not infer a fixed total allocation count: element expressions can allocate, and zero-sized element types do not need a vector buffer allocation.
For the repeat arm, evaluate the element and count once, then build a vector
containing that many copies of the value. std::iter::repeat_n(element, count)
followed by collect::<Vec<_>>() is one approach and requires Clone.
The element expression is still evaluated when the count is zero. Cloning
shared handles can preserve sharing; it does not necessarily make independent
copies of the underlying data. This exercise does not require matching the
standard macro's internal implementation or exact number of clone calls.
Add the optional match guard
The matcher can capture the guard as an optional group:
($e:expr, $p:pat $(if $g:expr)?) => {
match $e {
$p $(if $g)? => true,
_ => false,
}
};
This is a macro arm, not a standalone Rust item. The same optional group
appears in the expansion, between the pattern and =>. If the pattern
matches, its guard decides whether that arm applies. The fallback returns
false. The expression being matched is evaluated once.
if is permitted after a pat fragment. In macros defined in edition 2021
or later, pat also accepts top-level or-patterns such as 1 | 2. The edition
of the macro definition controls this behavior; this exercise uses a modern
edition. There is no need for a separate or-pattern arm.
Count map entries without evaluating them
The empty arm already returns HashMap::new(). For the nonempty arm,
reserve capacity for the number of supplied pairs, then insert them in
source order:
let mut m = ::std::collections::HashMap::with_capacity(count_tts!($($k),*));
$( m.insert($k, $v); )+
m
This is the body of a macro expansion. Put it inside a block so it can be
used as an expression. with_capacity reserves room for at least the
requested number of entries; it need not allocate an exactly sized table.
Repeated equal keys replace the earlier value, so the final length may be
smaller than the pair count. A map's iteration order is unspecified, which
is why the driver sorts its output.
count_tts! replaces each forwarded key expression with () and counts the
resulting array. It does not evaluate those expressions. Each key and value
should be evaluated only by its insertion. The helper's tt matcher can
receive an expression forwarded by another macro as one fragment; it is not
a general parser for arbitrary ungrouped expressions typed directly into it.
Keep the separate empty arm. It avoids generating a mutable local with no
insertions, which can trigger unused_mut. This is a simple way to satisfy
the warning gate, rather than a claim that an empty macro must always have
its own arm.
Check expansion behavior
Check empty forms, trailing commas, repeat counts, guards and or-patterns. For reusable macros, also check expressions with side effects to make sure counting or matching does not evaluate them twice. Lint behavior varies by lint and expansion context; macros are not generally exempt from warnings. The completed exercise must compile, produce the expected 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.