Macro quote
macro_rules! quote {
($($tt:tt)*) => { ... };
}
The whole point.
Performs variable interpolation against the input and produces it as
proc_macro2::TokenStream.
Note: for returning tokens to the compiler in a procedural macro, use
.into() on the result to convert to proc_macro::TokenStream.
Interpolation
Variable interpolation is done with #var (similar to $var in
macro_rules! macros). This grabs the var variable that is currently in
scope and inserts it in that location in the output tokens. Any type
implementing the ToTokens trait can be interpolated. This includes most
Rust primitive types as well as most of the syntax tree types from the Syn
crate.
Repetition is done using #(...)* or #(...),* again similar to
macro_rules!. This iterates through the elements of any variable
interpolated within the repetition and inserts a copy of the repetition body
for each one. The variables in an interpolation may be a Vec, slice,
BTreeSet, or any Iterator.
#(#var)*— no separators#(#var),*— the character before the asterisk is used as a separator#( struct #var; )*— the repetition can contain other tokens#( #k => println!("{}", #v), )*— even multiple interpolations
Hygiene
Any interpolated tokens preserve the Span information provided by their
ToTokens implementation. Tokens that originate within the quote!
invocation are spanned with Span::call_site().
A different span can be provided through the [quote_spanned!] macro.
Return type
The macro evaluates to an expression of type proc_macro2::TokenStream.
Meanwhile Rust procedural macros are expected to return the type
proc_macro::TokenStream.
The difference between the two types is that proc_macro types are entirely
specific to procedural macros and cannot ever exist in code outside of a
procedural macro, while proc_macro2 types may exist anywhere including
tests and non-macro code like main.rs and build.rs. This is why even the
procedural macro ecosystem is largely built around proc_macro2, because
that ensures the libraries are unit testable and accessible in non-macro
contexts.
There is a From-conversion in both directions so returning the output of
quote! from a procedural macro usually looks like tokens.into() or
proc_macro::TokenStream::from(tokens).
Examples
Procedural macro
The structure of a basic procedural macro is as follows. Refer to the Syn
crate for further useful guidance on using quote! as part of a procedural
macro.
#
extern crate proc_macro;
# extern crate proc_macro2;
#
use TokenStream;
# use TokenStream;
use quote;
# const IGNORE_TOKENS: &'static str = stringify! ;
Combining quoted fragments
Usually you don't end up constructing an entire final TokenStream in one
piece. Different parts may come from different helper functions. The tokens
produced by quote! themselves implement ToTokens and so can be
interpolated into later quote! invocations to build up a final result.
# use quote;
#
let type_definition = quote! ;
let methods = quote! ;
let tokens = quote! ;
Constructing identifiers
Suppose we have an identifier ident which came from somewhere in a macro
input and we need to modify it in some way for the macro output. Let's
consider prepending the identifier with an underscore.
Simply interpolating the identifier next to an underscore will not have the
behavior of concatenating them. The underscore and the identifier will
continue to be two separate tokens as if you had written _ x.
# use ;
# use quote;
#
# let ident = new;
#
// incorrect
quote!
# ;
The solution is to build a new identifier token with the correct value. As
this is such a common case, the [format_ident!] macro provides a
convenient utility for doing so correctly.
# use ;
# use ;
#
# let ident = new;
#
let varname = format_ident!;
quote!
# ;
Alternatively, the APIs provided by Syn and proc-macro2 can be used to
directly build the identifier. This is roughly equivalent to the above, but
will not handle ident being a raw identifier.
# use ;
# use quote;
#
# let ident = new;
#
let concatenated = format!;
let varname = new;
quote!
# ;
Making method calls
Let's say our macro requires some type specified in the macro input to have
a constructor called new. We have the type in a variable called
field_type of type syn::Type and want to invoke the constructor.
# use quote;
#
# let field_type = quote!;
#
// incorrect
quote!
# ;
This works only sometimes. If field_type is String, the expanded code
contains String::new() which is fine. But if field_type is something
like Vec<i32> then the expanded code is Vec<i32>::new() which is invalid
syntax. Ordinarily in handwritten Rust we would write Vec::<i32>::new()
but for macros often the following is more convenient.
# use quote;
#
# let field_type = quote!;
#
quote!
# ;
This expands to <Vec<i32>>::new() which behaves correctly.
A similar pattern is appropriate for trait methods.
# use quote;
#
# let field_type = quote!;
#
quote!
# ;
Interpolating text inside of doc comments
Neither doc comments nor string literals get interpolation behavior in quote:
quote! {
/// try to interpolate: #ident
///
/// ...
}
quote! {
#[doc = "try to interpolate: #ident"]
}
Instead the best way to build doc comments that involve variables is by formatting the doc string literal outside of quote.
# use ;
# use quote;
#
# const IGNORE: &str = stringify! ;
#
# let ident = new;
# let msg = format!;
quote!
# ;
Indexing into a tuple struct
When interpolating indices of a tuple or tuple struct, we need them not to
appears suffixed as integer literals by interpolating them as syn::Index
instead.
let i = 0usize..self.fields.len();
// expands to 0 + self.0usize.heap_size() + self.1usize.heap_size() + ...
// which is not valid syntax
quote! {
0 #( + self.#i.heap_size() )*
}
# use ;
# use quote;
#
#
#
#
#
#