ProgrammatieRust applicatie/library architect

Wat zijn macro's in Rust? Typen macro's, verschil tussen procedurele en declaratieve macro's, wanneer je de voorkeur moet geven aan de ene boven de andere en welke gevaren zich kunnen voordoen bij het gebruik ervan?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

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:

  • Declaratieve macro's (macro_rules!): worden gedefinieerd met behulp van een match-achtige syntaxis en werken volgens het principe van sjabloon->vervanging. De meest typische vorm is macro_rules!.
  • Procedurele macro's: worden gedefinieerd als externe functies in crate, krijgen AST (TokenStream) en retourneren gewijzigde code. Ze zijn verdeeld in #[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.

Misleidende vraag

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 } }

Voorbeelden van echte fouten door onbekendheid met de nuances van het onderwerp


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.