The `proc_macro` crate supports procedural macros: Rust code that generates Rust syntax at compile time. A procedural macro must be defined in a separate library crate, which can be a workspace member. section 8.3
Procedural macros come in three forms, with different placement rules:
- Function-like macros use `macro!()`, `macro!{}`, or `macro![]` syntax and can appear anywhere in code. section 8.3
- Derive macros attach to struct or enum declarations and can inject code. section 8.3
- Attribute macros attach to an existing item, can appear broadly, and accept arguments. 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
}
A small function-like macro accepts a token stream and returns generated Rust syntax as another token stream. 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
}
A derive implementation parses its input, builds generated code, and converts that code back into a token stream. section 8.3
