In Rust ermöglichen es Makros, Code zur Compile-Zeit zu generieren, und bieten leistungsstarke Werkzeuge für Metaprogrammierung, Reduzierung von Boilerplate und Implementierung von DSL. Die Hauptarten von Makros:
macro_rules!.#[derive], Attribut-Makros (#[some_macro]) und funktionsähnliche Makros (custom_macro!()).Deklarative Makros sind einfacher für boilerplateartigen Code zu verwenden, während prozedurale Makros mehr Kontrolle über die Syntax und die Token-Analyse bieten.
Beispiel eines deklarativen Makros:
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>
Beispiel eines prozeduralen Makros (Beispiel derive):
#[derive(Debug, Clone)] struct MyStruct; // der derive Debug ist im std durch ein prozedurales Makro implementiert.
Können Rust-Makros syntaktisch inkorrekten Code oder Code mit Laufzeitfehlern generieren?
Antwort: Ja, Makros überprüfen nicht die Korrektheit der Entfaltung zum Zeitpunkt des Schreibens; Fehler können erst nach der Substitution des Makros durch den Compiler auftreten. Deklarative Makros können zu unklaren syntaktischen Fehlern führen. Prozedurale Makros können inkorrekten oder verwundbaren Code generieren, sodass es entscheidend wichtig ist, ihre Funktionalität sorgfältig zu testen.
Beispiel:
macro_rules! make_error { () => { let x = ; // syntaktischer Fehler tritt bei der Verwendung des Makros auf } }
Geschichte
In einem großen Projekt wurde zur Reduzierung von Boilerplate macro_rules! verwendet, ohne alle Musterfälle abzudecken. Ein Benutzer übergab versehentlich einen nicht unterstützten Ausdruck an das Makro, was zu einem unverständlichen Kompilierungsfehler führte, dessen Ursache schwer nachzuvollziehen war.
Geschichte
Beim Übertragen prozeduraler Makros zwischen Crates traten Kompatibilitätsprobleme mit den Versionen der TokenStream-API auf, was dazu führte, dass die IDE abstürzte, und der Fehler zeigte sich nur bei no_std-Bauten.
Geschichte
Bei der Erstellung eines DSL für Konfigurationen mit einem prozeduralen Makro wurde eine unsichere Analyse der Eingabetokens (ohne Typvalidierung) implementiert, was zur Folge hatte, dass zur Laufzeit seltsame Bugs, Sicherheitsanfälligkeiten und die Unmöglichkeit auftraten, einen Teil der neuen Funktionalität korrekt bereitzustellen.