CodeBlanks Logo Icon CodeBlanks

derive macro

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

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
#![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

Question
&[$(____!($alan)),*]
Answer
&[$(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

All lessons in Advanced