Rust
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.
Exercise 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 {:?}.
Show example 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());
}
Test yourself
-
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
- Yes
Show answer
No; it only adds new items, the struct stays as it is — A macro that wants to change the item must be an attribute macro.
-
Turn field names into text
&[$(____!($alan)),*]
Show answer
&[$(stringify!($alan)),*]
-
Tool and role
- syn
- Parses tokens
- quote!
- Generates code
- DeriveInput
- Struct/enum tree
- #(#adlar),*
- quote! repetition
Next lesson: attribute macro