Types passed directly to macros pattern match the way you would expect, but if they're passed through another macro as ty
, they stop matching:
macro_rules! mrtype {
( bool ) => ("b");
( i32 ) => ("i");
( f64 ) => ("f");
( &str ) => ("z");
( $_t:ty ) => ("o");
}
macro_rules! around {
( $t:ty ) => (mrtype!($t));
}
fn main() {
println!("{}{}{}", mrtype!(i32), around!(i32), around!(&str));
}
This prints ioo
instead of iiz
.
Passing tt
s instead of ty
s works, but if you have &str
you need 2 tt
s, making everything unnecessarily complicated.
Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang ! , but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program.
The most widely used form of macros in Rust is the declarative macro. These are also sometimes referred to as “macros by example,” “ macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression.
Another important difference between macros and functions is that you must define macros or bring them into scope before you call them in a file, as opposed to functions you can define anywhere and call anywhere. Save this answer.
No, macros operate on syntax only. If there is a matcher that is ($arg:expr, &str) , the latter half of that expects a literal &str to be provided — it does not indicate that $arg is a string.
This doesn't work and cannot be made to work.
To summarise the Captures and Expansion Redux chapter of The Little Book of Rust Macros: the problem is that with the exception of tt
and ident
captures, macro_rules!
is completely unable to destructure or examine captured tokens. Once you capture something as ty
, it irrevocably becomes a black box to macro_rules!
.
To put it another way: &str
is not a type, as far as macro_rules!
is concerned: it's two tokens, &
and str
. When you capture and then substitute &str
as ty
, though, it becomes a single "meta token": the type &str
. The two are no longer the same, and as such don't match.
If you intend to later match against or destructure tokens, you must capture them as tt
s or ident
s (if possible). In this specific case, you could rewrite the rule for around
to instead be ($($t:tt)*) => (mrtype!($($t)*));
, which preserves the original token sequence.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With