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.
}