Rust
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" });
Exercise 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.
Show example solution
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);
}
Test yourself
-
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))]
- As a separate macro
Show answer
With #[proc_macro_derive(Name, attributes(serde))] — Registered helper attributes aren't rejected by the compiler, and the macro reads them from DeriveInput.
-
Helper attribute
#[proc_macro_derive(Serialize, ____(serde))]
pub fn serialize(girdi: TokenStream) -> TokenStream { .. }
Show answer
#[proc_macro_derive(Serialize, attributes(serde))]
pub fn serialize(girdi: TokenStream) -> TokenStream { .. }
-
Group summary
- proc-macro = true
- Macro crate
- TokenStream
- Input and output
- syn
- Parsing
- quote!
- Generation
Next lesson: dyn Trait