liveBook · Manning

Proc_macro

As explained in Idiomatic Rust

proc_macro is Rust's library for implementing procedural macros and manipulating their input and output token streams.

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

How Idiomatic Rust covers it

How deep it goes
Introduces macro forms and token-stream implementation, then sketches a derive example while deferring the broader topic.
Examples
Demonstrates a function-like macro that generates a print statement and a derive macro that implements a trait.
What you can do after
Build a basic procedural macro by transforming input tokens into generated Rust syntax.
How it connects
Connects procedural macros to derive macros, attribute macros, function-like macros, and token streams.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.