quote! Macro: Generate Rust Code Programmatically
The quote! macro is a compile-time code generator that converts Rust expressions and variables into a TokenStream. Instead of manually building a token tree or concatenating strings (error-prone and fragile), you write familiar Rust syntax inside quote! and interpolate variables with #. This produces type-safe, hygenic code that the compiler verifies. The quote! macro is the perfect complement to syn's parsing: syn reads code structure, quote! emits new code based on that structure.
Core Concepts: Interpolation and Token Splicing
quote! is not string interpolation. It operates on Rust's token stream, preserving type information and hygienic scoping. When you write quote! { let x = #value; }, the #value is not converted to a string; instead, the TokenStream representing value is spliced directly into the output.
Simple Interpolation
use quote::quote;
let name = syn::Ident::new("User", proc_macro2::Span::call_site());
let field_name = syn::Ident::new("email", proc_macro2::Span::call_site());
let code = quote! {
impl #name {
fn get_email(&self) -> &str {
&self.#field_name
}
}
};
println!("{}", code);
// Expands to: impl User { fn get_email(&self) -> &str { &self.email } }
Here, #name and #field_name are replaced with their token representations. The result is a proc_macro2::TokenStream.
Repeating with #(...)*
Just like in macro_rules!, you can repeat code using #(...)* syntax:
use quote::quote;
let field_names = vec![
syn::Ident::new("x", proc_macro2::Span::call_site()),
syn::Ident::new("y", proc_macro2::Span::call_site()),
syn::Ident::new("z", proc_macro2::Span::call_site()),
];
let field_getters = quote! {
#(
pub fn get_#field_names(&self) -> f64 {
self.#field_names
}
)*
};
println!("{}", field_getters);
// Expands to three getter methods, one for each field
Repetition with Separators
You can also repeat with separators:
let field_names = vec![
syn::Ident::new("name", proc_macro2::Span::call_site()),
syn::Ident::new("age", proc_macro2::Span::call_site()),
];
let field_list = quote! {
#(#field_names),* // Comma-separated: name, age
};
Building Trait Implementations
A common pattern combines syn parsing with quote! generation to auto-implement traits. Here is a complete example for a Display derive:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields};
#[proc_macro_derive(Display)]
pub fn derive_display(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let display_impl = match &input.data {
Data::Struct(data) => {
match &data.fields {
Fields::Named(fields) => {
let field_names: Vec<_> = fields
.named
.iter()
.map(|f| f.ident.as_ref().unwrap())
.collect();
let field_displays = quote! {
#(
write!(f, "{}: {}, ", stringify!(#field_names), self.#field_names)?;
)*
};
quote! {
write!(f, "{} {{ ", stringify!(#name))?;
#field_displays
write!(f, "}}")
}
}
Fields::Unnamed(fields) => {
let count = fields.unnamed.len();
let indices: Vec<_> = (0..count)
.map(syn::Index::from)
.collect();
quote! {
write!(f, "{}(", stringify!(#name))?;
#(
write!(f, "{}", self.#indices)?;
#( write!(f, ", ")? )*
)*
write!(f, ")")
}
}
Fields::Unit => {
quote! {
write!(f, "{}", stringify!(#name))
}
}
}
}
_ => quote! { write!(f, "not a struct") },
};
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics std::fmt::Display for #name #ty_generics #where_clause {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
#display_impl
}
}
};
TokenStream::from(expanded)
}
Advanced Patterns: Conditional Code Generation
You can use Rust's if and match statements to conditionally generate code:
use quote::quote;
use syn::{Fields, Data};
let input = parse_macro_input!(input as DeriveInput);
let impl_code = if let Data::Struct(data) = &input.data {
if let Fields::Named(fields) = &data.fields {
if fields.named.is_empty() {
quote! {
fn is_empty(&self) -> bool { true }
}
} else {
quote! {
fn is_empty(&self) -> bool { false }
}
}
} else {
quote! { }
}
} else {
quote! { compile_error!("Only structs are supported"); }
};
Handling Rust Identifiers and Paths
When interpolating identifiers and paths, quote! respects Rust's path resolution. If you have a syn::Path, you can splice it directly:
use quote::quote;
use syn::Path;
let trait_path: Path = syn::parse_str("std::fmt::Display").unwrap();
let name = syn::Ident::new("MyType", proc_macro2::Span::call_site());
let impl_code = quote! {
impl #trait_path for #name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "MyType instance")
}
}
};
Important: Span and Hygiene
By default, quote! uses Span::call_site(), which attaches error messages to the macro invocation site. This is usually correct, but for complex macros, you might want to preserve the original span:
use proc_macro2::Span;
use quote::quote;
// Call site span: errors point to the macro invocation
let code1 = quote! { let x = 5; };
// Using the span of an original token from the input
let input = parse_macro_input!(input as DeriveInput);
let original_span = input.span();
let code2 = quote::quote_spanned! { original_span =>
let x = 5;
};
Practical Example: Builder Pattern Generator
Here is a complete example that uses quote! to generate a builder pattern:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields};
#[proc_macro_derive(Builder)]
pub fn derive_builder(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let builder_name = syn::Ident::new(
&format!("{}Builder", name),
proc_macro2::Span::call_site(),
);
let (builder_fields, builder_methods, build_code) = match &input.data {
Data::Struct(data) => {
if let Fields::Named(fields) = &data.fields {
let names: Vec<_> = fields.named.iter().map(|f| f.ident.as_ref().unwrap()).collect();
let types: Vec<_> = fields.named.iter().map(|f| &f.ty).collect();
let builder_fields = quote! {
#( #names: Option<#types> ),*
};
let builder_methods = quote! {
#(
pub fn #names(mut self, #names: #types) -> Self {
self.#names = Some(#names);
self
}
)*
};
let build_code = quote! {
#name {
#( #names: self.#names.expect("missing field") ),*
}
};
(builder_fields, builder_methods, build_code)
} else {
(quote! {}, quote! {}, quote! {})
}
}
_ => (quote! {}, quote! {}, quote! {}),
};
let expanded = quote! {
pub struct #builder_name {
#builder_fields
}
impl #builder_name {
pub fn new() -> Self {
Self {
#( #builder_fields: None ),*
}
}
#builder_methods
pub fn build(self) -> #name {
#build_code
}
}
};
TokenStream::from(expanded)
}
Key Takeaways
quote!converts Rust syntax and variables into token streams without string interpolation, ensuring type safety- Interpolate variables with
#variableand repeat code with#(...)*syntax - Combine
synfor parsing withquote!for code generation: a powerful pattern for procedural macros - Use
Span::call_site()by default; usequote_spanned!when you need finer control over error locations - Conditional code generation uses normal Rust
if/matchstatements evaluated at macro time - The output of
quote!is aproc_macro2::TokenStream, which you convert toproc_macro::TokenStreamwith.into()orTokenStream::from()
Frequently Asked Questions
How do I interpolate a Rust identifier into a quote! macro?
Use #identifier directly. If you have a syn::Ident, splicing it with # produces the identifier's name as tokens. For strings or other types, you may need to construct an Ident first.
Can I nest quote! macros?
Yes. You can call quote! inside another quote!, and the inner TokenStream will be spliced into the outer one. This is useful for building up complex code generation.
What is the difference between quote! and quote_spanned!?
quote! uses Span::call_site() by default; quote_spanned! accepts an explicit span. For better error messages, use quote_spanned! with the original token's span.
How do I generate code that references an external crate?
Splice the full path directly: quote! { #crate_path::module::function() }. If the path is a syn::Path, you can interpolate it as #path.
Is there a performance cost to using quote! over manual token building?
No. quote! is a compile-time macro that expands to efficient token-building code. The performance is identical to manual construction.