Fragment Specifiers
As shown in the [macro_rules
] chapter, Rust, as of 1.46, has 13 fragment specifiers. This section
will go a bit more into detail for some of them and tries to always show a few examples of what a
matcher can match with.
Note that capturing with anything but the
ident
,lifetime
andtt
fragments will render the captured AST opaque, making it impossible to further inspect it in future macro invocations.
item
The item
fragment simply matches any of Rust's
item definitions, not identifiers that refer to
items. Item examples:
macro_rules! items { ($($item:item)*) => (); } items! { struct Foo; enum Bar { Baz } impl Foo {} /*...*/ } fn main() {}
block
The block
fragment solely matches a block expression,
which consists of and opening {
brace, followed by any amount of statements and finally followed
by a closing }
brace.
macro_rules! blocks { ($($block:block)*) => (); } blocks! { {} { let zig; } { 2 } } fn main() {}
stmt
The statement
fragment solely matches a statement
without its trailing semicolon, unless its an item statement that requires one. What would be an
item statement that requires one? A Unit-Struct would be a simple one, as defining one requires a
trailing semicolon.
Let's use a simple example to show exactly what is meant with this. We use a macro that merely emits what it captures:
macro_rules! statements {
($($stmt:stmt)*) => ($($stmt)*);
}
fn main() {
statements! {
struct Foo;
fn foo() {}
let zig = 3
let zig = 3;
3
3;
if true {} else {}
{}
}
}
Expanding this, via the playground for example1, gives us roughly the following:
/* snip */
fn main() {
struct Foo;
fn foo() { }
let zig = 3;
let zig = 3;
;
3;
3;
;
if true { } else { }
{ }
}
From this we can tell a few things:
The first you should be able to see immediately is that while the stmt
fragment doesn't capture
trailing semicolons, it still emits them when required, even if the statement is already followed by
one. The simple reason for that is that semicolons on their own are already valid statements. So we
are actually invoking our macro here with not 8 statements, but 11!
Another thing you should be able to notice here is that the trailing semicolon of the struct Foo;
item statement is being matched, otherwise we would've seen an extra one like in the other cases.
This makes sense as we already said, that for item statements that require one, the trailing
semicolon will be matched with.
A last observation is that expressions get emitted back with a trailing semicolon, unless the expression solely consists of only a block expression or control flow expression.
The fine details of what was just mentioned here can be looked up in the reference.
See the debugging chapter for tips on how to do this.
pat
The pat
fragment matches any kind of pattern.
macro_rules! patterns { ($($pat:pat)*) => (); } patterns! { "literal" _ 0..5 ref mut PatternsAreNice } fn main() {}
expr
The expr
fragment matches any kind of expression
(Rust has a lot of them, given it is an expression orientated language).
macro_rules! expressions { ($($expr:expr)*) => (); } expressions! { "literal" funcall() future.await break 'foo bar } fn main() {}
ty
The ty
fragment matches any kind of type expression.
A type expression is the syntax with which one refers to a type in the language.
macro_rules! types { ($($type:ty)*) => (); } types! { foo::bar bool [u8] } fn main() {}
ident
The ident
fragment matches an identifier
or keyword.
macro_rules! idents { ($($ident:ident)*) => (); } idents! { // _ <- This is not an ident, it is a pattern foo async O_________O _____O_____ } fn main() {}
path
The path
fragment matches a so called TypePath
style path.
macro_rules! paths { ($($path:path)*) => (); } paths! { ASimplePath ::A::B::C::D G::<eneri>::C } fn main() {}
tt
The tt
fragment matches a TokenTree. If you need a refresher on what exactly a TokenTree was you
may want to revisit the TokenTree chapter of this
book. The tt
fragment is one of the most powerful fragments, as it can match nearly anything while
still allowing you to inspect the contents of it at a later state in the macro.
meta
The meta
fragment matches an attribute, to
be more precise, the contents of an attribute. You will usually see this fragment being used in a
matcher like #[$meta:meta]
or #![$meta:meta]
.
macro_rules! metas { ($($meta:meta)*) => (); } metas! { ASimplePath super::man path = "home" foo(bar) } fn main() {}
A neat thing about doc comments: They are actually attributes in the form of
#[doc="…"]
where the...
is the actual comment string, meaning you can act on doc comments in macros!
lifetime
The lifetime
fragment matches a lifetime or label.
It's quite similar to ident
but with a prepended '
.
macro_rules! lifetimes { ($($lifetime:lifetime)*) => (); } lifetimes! { 'static 'shiv '_ } fn main() {}
vis
The vis
fragment matches a possibly empty Visibility qualifier.
Emphasis lies on the possibly empty part. You can think of this fragment having an implicit ?
repetition to it, meaning you don't, and in fact cannot, wrap it in a direct repetition.
macro_rules! visibilities { // ∨~~Note this comma, since we cannot repeat a `vis` fragment on its own ($($vis:vis,)*) => (); } visibilities! { , pub, pub(crate), pub(in super), pub(in some_path), } fn main() {}
literal
The literal
fragment matches any literal expression.
macro_rules! literals { ($($literal:literal)*) => (); } literals! { -1 "hello world" 2.3 b'b' true } fn main() {}