CodeBlanks Logo Icon CodeBlanks

Summary - Procedural macros

Combine derive, syn and quote to write a HelloMacro derive.

Procedural macros summary

Procedural macros are functions written in a separate crate with proc-macro = true that run at compile time: TokenStream → TokenStream. Kinds: #[proc_macro_derive] (extra code for an item), #[proc_macro_attribute] (transforms an item), #[proc_macro] (function-like). syn parses, quote! generates, and errors are reported with a location via syn::Error. For simple repetition, macro_rules! is often enough.

A constructor with defaults

Many derive macros (e.g. derive_builder, smart-default) give fields default values. Let's build the same idea with a declarative macro:

macro_rules! varsayilanli {
    (struct $ad:ident { $($alan:ident : $tur:ty = $deger:expr),* $(,)? }) => {
        struct $ad { $($alan: $tur),* }
        impl $ad {
            fn new() -> Self {
                Self { $($alan: $deger),* }
            }
        }
    };
}

varsayilanli!(struct Ayar { port: u16 = 8080, ad: &'static str = "app" });

Practice Struct with defaults

Write the varsayilanli! macro above and generate the Ayar struct with it. Create it with Ayar::new() and print the port and ad fields separated by a space.

Starter code

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

Sample solution

main.rs
macro_rules! varsayilanli {
    (struct $ad:ident { $($alan:ident : $tur:ty = $deger:expr),* $(,)? }) => {
        struct $ad {
            $($alan: $tur),*
        }
        impl $ad {
            fn new() -> Self {
                Self {
                    $($alan: $deger),*
                }
            }
        }
    };
}

varsayilanli!(struct Ayar { port: u16 = 8080, ad: &'static str = "app" });

fn main() {
    let a = Ayar::new();
    println!("{} {}", a.port, a.ad);
}

Expected output

8080 app

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 How are a derive macro's helper attributes (<code>#[serde(rename = ..)]</code>) defined?

  • They can't be
  • With macro_rules!
  • With #[proc_macro_derive(Name, attributes(serde))] Correct answer
  • As a separate macro

Explanation: Registered helper attributes aren't rejected by the compiler, and the macro reads them from DeriveInput.

2 Helper attribute

Question
#[proc_macro_derive(Serialize, ____(serde))]
pub fn serialize(girdi: TokenStream) -> TokenStream { .. }
Answer
#[proc_macro_derive(Serialize, attributes(serde))]
pub fn serialize(girdi: TokenStream) -> TokenStream { .. }

Explanation: attributes(..) registers the derive macro's helper attributes.

3 Group summary

Term Meaning
proc-macro = true Macro crate
TokenStream Input and output
syn Parsing
quote! Generation

All lessons in Advanced