In Rust stellen macro's je in staat om code te genereren tijdens de compilatiefase, wat krachtige tools biedt voor metaprogrammering, het verminderen van boilerplate en de implementatie van DSL's. De belangrijkste typen macro's:
macro_rules!.#[derive], attribuut macro's (#[some_macro]) en function-like macro's (custom_macro!()).Declaratieve macro's zijn eenvoudiger in gebruik voor sjablooncodes, procedurele macro's bieden meer controle over de syntaxis en tokenanalyse.
Voorbeeld van een declaratieve macro:
macro_rules! vec_of_strings { ($($x:expr),*) => { { let mut v = Vec::new(); $(v.push($x.to_string());)* v } }; } let v = vec_of_strings!("a", "b"); // => Vec<String>
Voorbeeld van een procedurele macro (derive):
#[derive(Debug, Clone)] struct MyStruct; // de derive Debug is geïmplementeerd met een procedurele macro in std.
Kunnen Rust macro's syntactisch onjuiste code of code met runtime-fouten genereren?
Antwoord: Ja, macro's controleren de juistheid van de expansie niet op het moment van schrijven; fouten kunnen zich pas openbaren na het substitueren van de macro door de compiler. Declaratieve macro's kunnen leiden tot onduidelijke syntactische fouten. Procedurele macro's kunnen onjuiste of kwetsbare code genereren, daarom is het cruciaal om hun werking grondig te testen.
Voorbeeld:
macro_rules! make_error { () => { let x = ; // syntactische fout zal optreden bij het gebruik van de macro } }
Verhaal
In een groot project, om boilerplate te reduceren, gebruikten ze macro_rules!, zonder alle gevallen van patronen te dekken. Een gebruiker gaf per ongeluk een niet-ondersteund expressie door aan de macro, wat leidde tot een onduidelijke compilatiefout, waarvan het moeilijk was de oorzaak te traceren.
Verhaal
Bij het overzetten van procedurele macro's tussen crates deden zich compatibiliteitsproblemen voor met versies van de TokenStream API, waardoor de IDE vastliep en de fout zich alleen manifesteerde bij no_std-builds.
Verhaal
Bij het schrijven van een DSL voor configuraties met een procedurele macro werd een onveilige parsing van binnenkomende tokens geïmplementeerd (zonder type-validatie), wat leidde tot vreemde bugs, kwetsbaarheden en de onmogelijkheid om een deel van de nieuwe functionaliteit correct uit te rollen.