Exercise 7: More Repetition
This exercise is going to also cover writing repetitions, but now involving more than one metavariable. Don't worry: the syntax is the exact same as what you've seen before.
Before you start, let's just quickly cover the different ways you can use a metavariable within a repetition.
Multiple Metavariables in One Repetition
You can indicate that two metavariables should be used in a single repetition.
For example, ( $($i:ident is $e:expr),+ )
would match my_macro!(pi is 3.14, tau is 6.28)
.
You would end up with $i
having matched pi
and tau
; and $e
having matched 3.14
and
6.28
.
Any repetition in the transcriber can use $i
, or $e
, or both within the same repetition.
So a transcriber could be $(let $i = $e;)+
; or let product = $($e)*+
One Metavariable Each, For Two Repetitions
Alternatively, you could specify two different repetitions, each containing their own metavariable. For example, this program will construct two vecs.
macro_rules! two_vecs { ($($vec1:expr),+; $($vec2:expr),+) => { { let mut vec1 = Vec::new(); $(vec1.push($vec1);)+ let mut vec2 = Vec::new(); $(vec2.push($vec2);)+ (vec1, vec2) } } } fn main() { let vecs = two_vecs!(1, 2, 3; 'a', 'b'); }
Importantly, with the above example, you have to be careful about using $vec1
and $vec2
in the same repetition within the transcriber. It is a compiler
error to use two metavariables captured a different number of times in the same
repetition.
To quote the reference:
Each repetition in the transcriber must contain at least one metavariable to decide how many times to expand it. If multiple metavariables appear in the same repetition, they must be bound to the same number of fragments. For instance,
( $( $i:ident ),* ; $( $j:ident ),* ) => (( $( ($i,$j) ),* ))
must bind the same number of$i
fragments as$j
fragments. This means that invoking the macro with(a, b, c; d, e, f)
is legal and expands to((a,d), (b,e), (c,f))
, but(a, b, c; d, e)
is illegal because it does not have the same number.
Exercise 7: More Repetition
In this task, you will be creating a hashmap
macro. It should consist
of comma-separated pairs, of the form literal => expr,
This should construct an empty HashMap
and insert
the
relevant key-value pairs.
You may not edit the main
function, but it should eventually look like the
following:
fn main() {
let value = "my_string";
let my_hashmap = {
let mut hm = HashMap::new();
hm.insert("hash", "map");
hm.insert("Key", value);
hm
};
print_hashmap(&my_hashmap);
}