Declarative and Procedural Macros in Depth

Generating Trait Implementations

Both declarative (macro_rules!) and procedural (#[proc_macro_derive]) macros are widely used to generate impl Trait for Type blocks across families of primitive types, tuples, newtypes, and generic structs without repetitive boilerplate.

Generating Trait Implementations Across Primitives and Newtypes with macro_rules!

In the standard library, traits on numeric types (u8 through u128, i8 through i128, f32, f64) and delegation traits on newtypes (From, AsRef, Display, arithmetic operators) are generated via declarative macros:

use std::fmt;
use std::ops::Add;

macro_rules! impl_id_newtype {
    ( $( $vis:vis struct $Name:ident ( $Inner:ty ) ; )+ ) => {
        $(
            #[repr(transparent)]
            #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
            $vis struct $Name(pub $Inner);

            impl ::std::convert::From<$Inner> for $Name {
                fn from(value: $Inner) -> Self {
                    Self(value)
                }
            }

            impl ::std::convert::From<$Name> for $Inner {
                fn from(wrapper: $Name) -> Self {
                    wrapper.0
                }
            }

            impl fmt::Display for $Name {
                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                    write!(f, "{}({})", stringify!($Name), self.0)
                }
            }

            impl Add<$Inner> for $Name {
                type Output = Self;
                fn add(self, rhs: $Inner) -> Self::Output {
                    Self(self.0 + rhs)
                }
            }
        )+
    };
}

impl_id_newtype! {
    pub struct OrderId(u64);
    pub struct ShardId(u16);
}

fn main() {
    let order = OrderId::from(1000) + 5;
    let shard = ShardId::from(3);

    println!("order = {}, shard = {}", order, shard);
}
order = OrderId(1005), shard = ShardId(3)

Handling Generic Parameters and where Clauses in Procedural Derive Macros

When generating a trait implementation impl<...> MyTrait for MyStruct<...> where ... inside a procedural derive macro, a generic struct definition contains three distinct components of syn::Generics:

Input Struct Definition:
  struct Container<'a, T: Clone = String, const N: usize = 4>
  where
      T: Send,
  { ... }

`input.generics.split_for_impl()` Produces Three Exact Components:
  1. `impl_generics`:  `<'a, T: Clone, const N: usize>` (Defaults stripped for `impl<...>`)
  2. `ty_generics`:    `<'a, T, N>`                     (Bounds & defaults stripped for `Container<...>`)
  3. `where_clause`:   `where T: Send`                  (Existing where clause, ready to append bounds)

If the derived trait requires each generic type parameter T to also implement MyTrait, clone input.generics, call .make_where_clause(), push T: MyTrait predicates for each type parameter, and then call .split_for_impl():

// Inside a `proc-macro = true` derive macro using `syn` and `quote`:
/*
fn add_trait_bounds(mut generics: syn::Generics) -> syn::Generics {
    for param in &mut generics.params {
        if let syn::GenericParam::Type(type_param) = param {
            type_param.bounds.push(syn::parse_quote!(::my_crate::Describe));
        }
    }
    generics
}
*/

// Resulting generated code uses fully qualified trait paths (`::std::...`)
// so the expansion never collides with local imports in the user's module:
trait Describe {
    fn describe(&self) -> String;
}

struct Pair<A, B> {
    first: A,
    second: B,
}

// Generated by `split_for_impl()` + `add_trait_bounds`:
impl<A: Describe, B: Describe> Describe for Pair<A, B> {
    fn describe(&self) -> String {
        ::std::format!(
            "Pair({}, {})",
            <A as Describe>::describe(&self.first),
            <B as Describe>::describe(&self.second),
        )
    }
}

impl Describe for i32 {
    fn describe(&self) -> String {
        self.to_string()
    }
}

fn main() {
    let p = Pair {
        first: 10,
        second: 20,
    };
    println!("{}", p.describe());
}
Pair(10, 20)