Makron låter dig skriva kod som skriver annan kod. Ta reda på om metaprogrammerings märkliga och kraftfulla värld.

Kodgenerering är en funktion som du hittar i de flesta moderna programmeringsspråk. Det kan hjälpa dig att minska kod och kodduplicering, definiera domänspecifika språk (DSL) och implementera ny syntax.

Rust tillhandahåller ett kraftfullt makrosystem som låter dig generera kod vid kompilering för mer sofistikerad programmering.

Introduktion till rostmakron

Makron är en typ av metaprogrammering som du kan använda för att skriva kod som skriver kod. I Rust är ett makro en kodbit som genererar annan kod vid kompilering.

Rostmakron är en kraftfull funktion som låter dig skriva kod som genererar annan kod vid kompilering för att automatisera repetitiva uppgifter. Rusts makron hjälper till att minska kodduplicering och öka kodunderhållbarhet och läsbarhet.

Du kan använda makron för att generera allt från enkla kodavsnitt till bibliotek och ramverk. Makron skiljer sig från Rostfunktioner eftersom de arbetar på kod snarare än data vid körning.

instagram viewer

Definiera makron i rost

Du kommer att definiera makron med makro_regler! makro. De makro_regler! makro tar ett mönster och en mall som indata. Rust matchar mönstret mot inmatningskoden och använder mallen för att generera utdatakoden.

Så här kan du definiera makron i Rust:

makro_regler! Säg hej {
() => {
println!("Hej världen!");
};
}

fnhuvud() {
Säg hej!();
}

Koden definierar en Säg hej makro som genererar kod för att skriva ut "Hej, värld!". Koden matchar () syntax mot en tom ingång och println! makro genererar utdatakoden.

Här är resultatet av att köra makrot i huvud fungera:

Makron kan ta ingångsargument för den genererade koden. Här är ett makro som tar ett enda argument och genererar kod för att skriva ut ett meddelande:

makro_regler! say_message {
($meddelande: expr) => {
println!("{}", $meddelande);
};
}

De say_meddelande makro tar $meddelande argument och genererar kod för att skriva ut argumentet med hjälp av println! makro. De expr syntax matchar argumentet mot alla Rust-uttryck.

Typer av rostmakron

Rust tillhandahåller tre typer av makron. Var och en av makrotyperna tjänar specifika syften, och de har sin syntax och begränsningar.

Procedurmakron

Procedurmakron anses vara den mest kraftfulla och mångsidiga typen. Procedurmakron låter dig definiera anpassad syntax som genererar Rustkod samtidigt. Du kan använda procedurmakron för att skapa anpassade härledda makron, anpassade attributliknande makron och anpassade funktionsliknande makron.

Du kommer att använda anpassade härledda makron för att implementera strukturer och uppräkningsegenskaper automatiskt. Populära paket som Serde använder ett anpassat härledande makro för att generera serialiserings- och deserialiseringskod för Rust-datastrukturer.

Anpassade attributliknande makron är praktiska för att lägga till anpassade kommentarer till Rust-kod. Rocket webbramverket använder ett anpassat attributliknande makro för att definiera rutter kortfattat och läsbart.

Du kan använda anpassade funktionsliknande makron för att definiera nya Rust-uttryck eller -satser. Lazy_static-lådan använder ett anpassat funktionsliknande makro för att definiera latinitialiserade statiska variabler.

Så här kan du definiera ett procedurmakro som definierar ett anpassat härledande makro:

använda sig av proc_macro:: TokenStream;
använda sig av citat:: citat;
använda sig av syn::{DeriveInput, parse_macro_input};

De använda sig av direktiv importerar nödvändiga lådor och typer för att skriva ett Rust-procedurmakro.

#[proc_macro_derive (MyTrait)]
pubfnmy_derive_macro(ingång: TokenStream) -> TokenStream {
låta ast = parse_macro_input!(input som DeriveInput);
låta namn = &ast.ident;

låta gen = citat! {
impl MyTrait för #namn {
// implementering här
}
};

gen.into()
}

Programmet definierar ett procedurmässigt makro som genererar implementeringen av en egenskap för en struktur eller enum. Programmet anropar makrot med namnet MyTrait i derive-attributet för strukturen eller enum. Makrot tar en TokenStream objekt som indata som innehåller koden tolkad i ett abstrakt syntaxträd (AST) med parse_macro_input! makro.

De namn variabel är den härledda struktur- eller enumidentifieraren, den Citat! Makrot genererar en ny AST som representerar implementeringen av MyTrait för den typ som så småningom returneras som en TokenStream med in i metod.

För att använda makrot måste du importera makrot från modulen där du deklarerade det:

// förutsatt att du deklarerade makrot i en my_macro_module-modul

använda sig av my_macro_module:: my_derive_macro;

När du deklarerar strukturen eller uppräkningen som använder makrot, lägger du till #[härleda (MyTrait)] attribut till toppen av deklarationen.

#[härleda (MyTrait)]
strukturMyStruct {
// fält här
}

struct-deklarationen med attributet expanderar till en implementering av MyTrait egenskap för strukturen:

impl MyTrait för MyStruct {
// implementering här
}

Implementeringen låter dig använda metoder i MyTrait egenskap på MyStruct instanser.

Attributmakron

Attributmakron är makron som du kan tillämpa på Rust-objekt som strukturer, enums, funktioner och moduler. Attributmakron har formen av ett attribut följt av en lista med argument. Makrot analyserar argumentet för att generera Rust-kod.

Du kommer att använda attributmakron för att lägga till anpassade beteenden och kommentarer till din kod.

Här är ett attributmakro som lägger till ett anpassat attribut till en Rust-struktur:

// importerar moduler för makrodefinitionen
använda sig av proc_macro:: TokenStream;
använda sig av citat:: citat;
använda sig av syn::{parse_macro_input, DeriveInput, AttributeArgs};

#[proc_macro_attribute]
pubfnmy_attribute_macro(attr: TokenStream, artikel: TokenStream) -> TokenStream {
låta args = parse_macro_input!(attr som AttributeArgs);
låta input = parse_macro_input!(objekt som DeriveInput);
låta namn = &input.ident;

låta gen = citat! {
#inmatning
impl #namn {
// anpassat beteende här
}
};

gen.into()
}

Makrot tar en lista med argument och en strukturdefinition och genererar en modifierad struktur med det definierade anpassade beteendet.

Makrot tar två argument som indata: attributet som tillämpas på makrot (tolkat med parse_macro_input! makro) och objektet (tolkat med parse_macro_input! makro). Makrot använder Citat! makro för att generera koden, inklusive den ursprungliga inmatningsposten och en extra impl block som definierar det anpassade beteendet.

Slutligen returnerar funktionen den genererade koden som en TokenStream med in i() metod.

Makroregler

Makroregler är den mest enkla och flexibla typen av makron. Makroregler låter dig definiera anpassad syntax som expanderar till Rustkod vid kompilering. Makroregler definierar anpassade makron som matchar alla rustuttryck eller uttalanden.

Du kommer att använda makroregler för att generera standardkod för att abstrahera detaljer på låg nivå.

Så här kan du definiera och använda makroregler i dina Rust-program:

makro_regler! make_vector {
( $( $x: expr ),* ) => {
{
låtamut v = Vec::ny();
$(
v.push($x);
)*
v
}
};
}

fnhuvud() {
låta v = make_vector![1, 2, 3];
println!("{:?}"v); // skriver ut "[1, 2, 3]"
}

Programmet definierar en make_vector! ett makro som skapar en ny vektor från en lista med kommaseparerade uttryck i huvud fungera.

Inuti makrot matchar mönsterdefinitionen argumenten som skickas till makrot. De $( $x: expr ),* syntax matchar alla kommaseparerade uttryck som identifieras som $x.

De $( ) syntaxen i expansionskoden itererar över varje uttryck i listan över argument som skickas till makrot efter den avslutande parentesen, vilket indikerar att iterationerna ska fortsätta tills makrot bearbetar alla uttryck.

Organisera dina rostprojekt effektivt

Rostmakron förbättrar kodorganisationen genom att du kan definiera återanvändbara kodmönster och abstraktioner. Makron kan hjälpa dig att skriva mer koncis, uttrycksfull kod utan dubbletter över olika projektdelar.

Du kan också organisera Rust-program i lådor och moduler för bättre kodorganisation, återanvändbarhet och samverkan med andra lådor och moduler.