In a macro pattern, it is also possible to match against an unlimited number of patterns, using the repetition operators + and *. They behave exactly like the same operators in regular expressions:
- + matches 1 or more times.
- * matches 0, 1, or more times.
Let's write a very useful macro, a macro to provide syntactic sugar to create HashMaps:
Note: A HashMap is a data structure from Rust's standard library that maps keys to values.
macro_rules! hash {
($( $key:expr => $value:expr ),*) => {{
let mut hashmap = ::std::collections::HashMap::new();
$(hashmap.insert($key, $value);)*
hashmap
}};
}
As we can see, we use the * operator here. The comma before it specify the separator token: this token must be present between each occurrence of the pattern between parentheses (which is the pattern that can be repeated). Don't forget the leading $ before the opening parenthesis; without it, the macro will match the literal (. Inside the parentheses, we see a normal pattern, an expression, followed by the => operator, followed by another expression. The body of this rule is particular, since it uses two pairs of curly brackets instead of only one.
First, let's look at how we use this macro, and we'll go back to this peculiarity right after:
let hashmap = hash! {
"one" => 1,
"two" => 2
};
If we were to use only one pair of curly brackets, like this:
macro_rules! hash {
($( $key:expr => $value:expr ),*) => {
let mut hashmap = ::std::collections::HashMap::new();
$(hashmap.insert($key, $value);)*
hashmap
};
}
The compiler will try to generate the following code, which doesn't compile:
let hashmap = let mut hashmap = ::std::collections::HashMap::new();
hashmap.insert("one", 1);
hashmap.insert("two", 2);
hashmap;
It doesn't compile because Rust wants an expression on the right-hand side of =. To transform this code into an expression, we simply need to add the curly brackets:
let hashmap = {
let mut hashmap = ::std::collections::HashMap::new();
hashmap.insert("one", 1);
hashmap.insert("two", 2);
hashmap
};
Hence the second pair of curly brackets.
There's one remaining line that requires an explanation in the body of the macro:
$(hashmap.insert($key, $value);)*
This means that the statement will be repeated as many times as there are pairs of key/values. Notice that ; is inside the parentheses; and there's no separator before * because every statement needs to end with a semicolon. But it's still possible to specify a separator here, as shown in the following example:
let keys = [$($key),*];
This will expand all the $keys, separating them by a comma. For instance, with a call like:
hash! {
"one" => 1,
"two" => 2
}
It will results in:
let keys = ["one", "two"];