I'm reading a book about Rust, and start playing with Rust macros. All metavariable types are explained there and have examples, except the last one – tt
. According to the book, it is a “a single token tree”. I'm curious, what is it and what is it used for? Can you please provide an example?
Rust has excellent support for macros. Macros enable you to write code that writes other code, which is known as metaprogramming. Macros provide functionality similar to functions but without the runtime cost. There is some compile-time cost, however, since macros are expanded during compile time.
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.
macro_rules allows users to define syntax extension in a declarative way. We call such extensions "macros by example" or simply "macros". Each macro by example has a name, and one or more rules.
That's a notion introduced to ensure that whatever is in a macro invocation correctly matches ()
, []
and {}
pairs. tt
will match any single token or any pair of parenthesis/brackets/braces with their content.
For example, for the following program:
fn main() { println!("Hello world!"); }
The token trees would be:
fn
main
()
{ println!("Hello world!"); }
println
!
("Hello world!")
"Hello world!"
;
Each one forms a tree where simple tokens (fn
, main
etc.) are leaves, and anything surrounded by ()
, []
or {}
has a subtree. Note that (
does not appear alone in the token tree: it's not possible to match (
without matching the corresponding )
.
For example:
macro_rules! { (fn $name:ident $params:tt $body:tt) => { /* … */ } }
would match the above function with $name → main
, $params → ()
, $body → { println!("Hello world!"); }
.
Token tree is the least demanding metavariable type: it matches anything. It's often used in macros which have a “don't really care” part, and especially in macros which have a “head” and a “tail” part. For example, the println!
macros have a branch matching ($fmt:expr, $($arg:tt)*)
where $fmt
is the format string, and $($arg:tt)*
means “all the rest” and is just forwarded to format_args!
. Which means that println!
does not need to know the actual format and do complicated matching with it.
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