Procedural macros for custom derives and attributes
Marcus Chen
Jan 2026
1 tab
// In a proc-macro crate:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyTrait)]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let expanded = quote! {
impl MyTrait for #name {
fn my_method(&self) {
println!("MyTrait for {}", stringify!(#name));
}
}
};
TokenStream::from(expanded)
}
1 file · rust
Explain with highlit
Procedural macros operate on Rust syntax trees, enabling custom #[derive(...)], attribute macros, or function-like macros. I write proc macros for boilerplate reduction: auto-generating builders, serialization, or validation. They're more powerful than declarative macros but require a separate crate with proc-macro = true. The syn crate parses Rust code, quote generates code, and proc-macro2 bridges the APIs. Proc macros run at compile time and can introspect types. I use them to enforce invariants, generate test cases, or create DSLs. They're the foundation of serde's derive macros and many popular crates. Writing them is advanced but worthwhile for widely-used patterns.