In $e:expr, the expr is called the fragment specifier. It tells the parser what kind of tokens the parameter $e is expecting. Rust provides a variety of fragment specifiers to, allowing the input to be very flexible.
| Specifier | Description | Examples |
|---|---|---|
ident | Identifier | x, foo |
path | Qualified name | std::collection::HashSet, Vec::new |
ty | Type | i32, &T, Vec<(char, String)> |
expr | Expression | 2+2, f(42), if true { 1 } else { 2 } |
pat | Pattern | _, c @ 'a' ... 'z', (true, &x), Badger { age, .. } |
stmt | Statement | let x = 3, return 42 |
block | Brace-delimited block | { foo(); bar(); }, { x(); y(); z() } |
item | Item | fn foo() {}, struct Bar;, use std::io; |
meta | Inside of attribute | cfg!(windows), doc="comment" |
tt | Token tree | +, foo, 5, [?!(???)] |
Note that a doc comment /// comment is treated the same as #[doc="comment"] to a macro.
macro_rules! declare_const_option_type {
(
$(#[$attr:meta])*
const $name:ident: $ty:ty as optional;
) => {
$(#[$attr])*
const $name: Option<$ty> = None;
}
}
declare_const_option_type! {
/// some doc comment
const OPT_INT: i32 as optional;
}
// The above will be expanded to:
#[doc="some doc comment"]
const OPT_INT: Option<i32> = None;
Some fragment specifiers requires the token following it must be one of a restricted set, called the "follow set". This allows some flexibility for Rust's syntax to evolve without breaking existing macros.
| Specifier | Follow set |
|---|---|
expr, stmt | => , ; |
ty, path | => , = | ; : > [ { as where |
pat | => , = | if in |
ident, block, item, meta, tt | any token |
macro_rules! invalid_macro {
($e:expr + $f:expr) => { $e + $f };
// ^
// `+` is not in the follow set of `expr`,
// and thus the compiler will not accept this macro definition.
($($e:expr)/+) => { $($e)/+ };
// ^
// The separator `/` is not in the follow set of `expr`
// and thus the compiler will not accept this macro definition.
}