derive macro
- Rust course
- Advanced
- Lesson 49/81
- 4 min
Write a derive macro with #[proc_macro_derive].
Derive macros
A real derive macro parses the struct and generates code from its fields. syn parses, quote generates:
#[proc_macro_derive(Alanlar)]
pub fn alanlar(girdi: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(girdi as syn::DeriveInput);
let ad = &ast.ident;
let syn::Data::Struct(veri) = &ast.data else { panic!("yalnızca struct") };
let adlar = veri.fields.iter().map(|f| f.ident.as_ref().unwrap().to_string());
quote::quote! {
impl #ad {
fn alanlar() -> &'static [&'static str] { &[#(#adlar),*] }
}
}.into()
}Inside quote!, #ad interpolates a variable and #(..),* repeats; it resembles macro_rules! syntax.
The same idea with macro_rules!
If we pass the whole struct definition to the macro, we can read the fields with macro_rules! too:
macro_rules! alanli_struct {
(struct $ad:ident { $($alan:ident : $tur:ty),* $(,)? }) => {
struct $ad { $($alan: $tur),* }
impl $ad {
fn alanlar() -> &'static [&'static str] {
&[$(stringify!($alan)),*]
}
}
};
}Its limits: a pattern must be written for every variation like generics, attributes and visibility; syn parses all of them.
Practice Field list
Write the alanli_struct! macro above and generate the definition struct Kisi { ad: String, yas: u32 } with it. Print the result of Kisi::alanlar() with {:?}.
Starter code
fn main() {
// buraya yaz
}Sample solution
#![allow(dead_code)]
macro_rules! alanli_struct {
(struct $ad:ident { $($alan:ident : $tur:ty),* $(,)? }) => {
struct $ad {
$($alan: $tur),*
}
impl $ad {
fn alanlar() -> &'static [&'static str] {
&[$(stringify!($alan)),*]
}
}
};
}
alanli_struct!(struct Kisi { ad: String, yas: u32 });
fn main() {
println!("{:?}", Kisi::alanlar());
}Expected output
["ad", "yas"]
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Can a derive macro change the input struct?
- Only the fields
- Only the name
- No; it only adds new items, the struct stays as it is Correct answer
- Yes
Explanation: A macro that wants to change the item must be an attribute macro.
2 Turn field names into text
&[$(____!($alan)),*]
&[$(stringify!($alan)),*]
Explanation: stringify! gives each field name as a &'static str.
3 Tool and role
| Term | Meaning |
|---|---|
syn |
Parses tokens |
quote! |
Generates code |
DeriveInput |
Struct/enum tree |
#(#adlar),* |
quote! repetition |