A procedural macro is Rust code that generates Rust code at compile time. It is useful when repetitive implementations would be tedious to write by hand, and it offers more flexibility than a declarative macro. The common #[derive(...)] syntax is one application. section 8.3 section 5.1
Choose a form according to where the generated code belongs:
- A function-like macro uses a call such as name!() and can act like a function or code block. section 8.3
- A derive macro attaches to a struct or enum declaration and can generate code following it. section 8.3
- An attribute macro attaches to an existing item and can accept arguments. section 8.3
Define the macro in a separate library crate, not a binary crate, and set proc-macro = true under [lib]. Its exported function uses proc_macro::TokenStream for input and output. For a small macro, the output can be produced by parsing a string of Rust syntax. section 8.3
use proc_macro::TokenStream;
#[proc_macro] #1
pub fn say_hello_world(_item: TokenStream) #2
-> TokenStream { #2
"println!(\"hello world\\")".parse().unwrap() #3
}
The #[proc_macro] function ignores its input and returns tokens for a println! invocation—the smallest example of code generating code. section 8.3
[lib] proc-macro = true
This [lib] setting marks the library as a procedural-macro crate. section 8.3
Deriving a trait implementation
A PrintName derive parses its input, handles the type’s generics, and uses quote! to generate a PrintName implementation. The generated name() method returns the struct’s name; the trait’s default print_name() method prints it. Compare the derive invocation with the equivalent handwritten implementation to see what the macro saves. section 8.3
Listing 8.9 Implementing the PrintName derive macro
#[proc_macro_derive(PrintName)]
pub fn print_name(input: TokenStream) -> TokenStream { #1
let input = parse_macro_input!(input as DeriveInput); #1
let generics = add_trait_bounds(input.generics); #2
let (impl_generics, type_generics, where_clause) = #3
generics.split_for_impl(); #3
let name = input.ident;
let expanded = quote! { #4
impl #impl_generics print_name::PrintName for #name #type_generics
#where_clause {
fn name() -> &'static str {
stringify!(#name) #5
}
}
};
TokenStream::from(expanded) #6
}
fn add_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push( #7
parse_quote!(print_name::PrintName) #7
); #7
}
}
generics
}
The derive implementation shows input parsing, generic-parameter handling, quoted Rust syntax, and conversion back to TokenStream. section 8.3
Reach for a procedural macro when generated code earns back its complexity. Macros can reduce repetition, but they can also hide code smells; compiler errors become harder to follow as macros grow more complex. section 5.1 section 8.3

