Makri vam omogočajo pisanje kode, ki piše drugo kodo. Spoznajte čuden in močan svet metaprogramiranja.

Generiranje kode je funkcija, ki jo boste našli v večini sodobnih programskih jezikov. Pomaga vam lahko zmanjšati standardno kodo in podvajanje kode, definirati domensko specifične jezike (DSL) in implementirati novo sintakso.

Rust ponuja zmogljiv makro sistem, ki vam omogoča ustvarjanje kode v času prevajanja za bolj sofisticirano programiranje.

Uvod v makre Rust

Makri so vrsta metaprogramiranja, ki ga lahko uporabite za pisanje kode, ki piše kodo. V Rustu je makro del kode, ki med prevajanjem ustvari drugo kodo.

Makri Rust so zmogljiva funkcija, ki vam omogoča pisanje kode, ki generira drugo kodo v času prevajanja za avtomatizacijo ponavljajočih se nalog. Rustovi makri pomagajo zmanjšati podvajanje kode ter povečajo vzdržljivost in berljivost kode.

Makre lahko uporabite za ustvarjanje česar koli, od preprostih izrezkov kode do knjižnic in ogrodij. Makri se razlikujejo od Funkcije rje ker med izvajanjem delujejo na kodi in ne na podatkih.

instagram viewer

Definiranje makrov v Rust

Makre boste definirali z makro_pravila! makro. The makro_pravila! makro sprejme vzorec in predlogo kot vhod. Rust primerja vzorec z vhodno kodo in uporabi predlogo za ustvarjanje izhodne kode.

Tukaj je opisano, kako lahko definirate makre v Rust:

makro_pravila! reci živijo {
() => {
println!("Pozdravljen, svet!");
};
}

fnglavni() {
reci živijo!();
}

Kodeks opredeljuje a reci živijo makro, ki generira kodo za tiskanje "Hello, world!". Koda se ujema z () sintakso proti praznemu vnosu in println! makro ustvari izhodno kodo.

Tukaj je rezultat izvajanja makra v glavni funkcija:

Makri lahko sprejmejo vhodne argumente za ustvarjeno kodo. Tukaj je makro, ki sprejme en argument in ustvari kodo za tiskanje sporočila:

makro_pravila! say_message {
($sporočilo: izraz) => {
println!("{}", $sporočilo);
};
}

The reci_sporočilo makro prevzame $sporočilo in ustvari kodo za tiskanje argumenta z uporabo println! makro. The ekspr sintaksa se ujema z argumentom proti kateremu koli izrazu Rust.

Vrste makrov Rust

Rust ponuja tri vrste makrov. Vsak tip makra služi posebnim namenom in ima svojo sintakso ter omejitve.

Proceduralni makri

Proceduralni makri veljajo za najmočnejšo in vsestransko uporabno vrsto. Proceduralni makri vam omogočajo, da definirate sintakso po meri, ki hkrati generira kodo Rust. Proceduralne makre lahko uporabite za ustvarjanje izpeljanih makrov po meri, makrov, podobnih atributom, in makrov, podobnih funkcijam.

Uporabili boste izpeljane makre po meri za samodejno implementacijo struktur in lastnosti enum. Priljubljeni paketi, kot je Serde, uporabljajo makro za izpeljavo po meri za ustvarjanje serializacijske in deserializacijske kode za podatkovne strukture Rust.

Makri, podobni atributom po meri, so priročni za dodajanje opomb po meri v kodo Rust. Spletno ogrodje Rocket uporablja makro, podoben atributom po meri, za jedrnato in berljivo definiranje poti.

Za definiranje novih izrazov ali stavkov Rust lahko uporabite makre, podobne funkcijam po meri. Zaboj Lazy_static uporablja makro funkciji po meri za definiranje leno inicializirano statične spremenljivke.

Tukaj je opisano, kako lahko definirate proceduralni makro, ki definira izpeljani makro po meri:

uporaba proc_macro:: TokenStream;
uporaba citat:: citat;
uporaba syn::{DeriveInput, parse_macro_input};

The uporaba direktive uvozijo potrebne zaboje in vrste za pisanje proceduralnega makra Rust.

#[proc_macro_derive (MyTrait)]
pubfnmoj_izpeljani_makro(vnos: TokenStream) -> TokenStream {
pustiti ast = parse_macro_input!(vnos kot DeriveInput);
pustiti ime = &ast.ident;

pustiti gen = citat! {
impl MyTrait za #ime {
// implementacija tukaj
}
};

gen.into()
}

Program definira proceduralni makro, ki generira implementacijo lastnosti za strukturo ali enum. Program prikliče makro z imenom MyTrait v izpeljanem atributu struct ali enum. Makro traja a TokenStream objekt kot vhod, ki vsebuje kodo, razčlenjeno v drevo abstraktne sintakse (AST) z parse_macro_input! makro.

The ime spremenljivka je izpeljana struktura ali identifikator enum, kvota! Makro ustvari nov AST, ki predstavlja implementacijo MyTrait za vrsto, ki je na koncu vrnjena kot a TokenStream z v metoda.

Če želite uporabiti makro, ga boste morali uvoziti iz modula, v katerem ste ga deklarirali:

// ob predpostavki, da ste makro deklarirali v modulu my_macro_module

uporaba my_macro_module:: my_derive_macro;

Ko deklarirate strukturo ali enum, ki uporablja makro, boste dodali #[izpelji (moja lastnost)] atribut na vrhu deklaracije.

#[izpelji (moja lastnost)]
structMyStruct {
// polja tukaj
}

Deklaracija struct z atributom se razširi na izvedbo MyTrait lastnost za strukturo:

impl MyTrait za MyStruct {
// implementacija tukaj
}

Izvedba vam omogoča uporabo metod v MyTrait lastnost na MyStruct primerki.

Makri atributov

Makri atributov so makri, ki jih lahko uporabite za predmete Rust, kot so strukture, enumi, funkcije in moduli. Makri atributov so v obliki atributa, ki mu sledi seznam argumentov. Makro razčleni argument, da ustvari kodo Rust.

Uporabili boste makre atributov, da boste svoji kodi dodali vedenje po meri in opombe.

Tu je makro atributa, ki strukturi Rust doda atribut po meri:

// uvoz modulov za definicijo makra
uporaba proc_macro:: TokenStream;
uporaba citat:: citat;
uporaba syn::{parse_macro_input, DeriveInput, AttributeArgs};

#[proc_macro_attribute]
pubfnmoj_atribut_makro(attr: TokenStream, element: TokenStream) -> TokenStream {
pustiti args = parse_macro_input!(attr kot AttributeArgs);
pustiti vnos = razčleni_makro_vnos!(element kot DeriveInput);
pustiti ime = &input.ident;

pustiti gen = citat! {
#vnos
impl #ime {
// vedenje po meri tukaj
}
};

gen.into()
}

Makro vzame seznam argumentov in definicija strukture in ustvari spremenjeno strukturo z definiranim vedenjem po meri.

Makro kot vhod sprejme dva argumenta: atribut, uporabljen za makro (razčlenjen z parse_macro_input! makro) in element (razčlenjen z parse_macro_input! makro). Makro uporablja kvota! makro za ustvarjanje kode, vključno s prvotnim vhodnim elementom in dodatnim impl blok, ki določa vedenje po meri.

Končno funkcija vrne ustvarjeno kodo kot a TokenStream z v () metoda.

Makro pravila

Makro pravila so najbolj enostavna in prilagodljiva vrsta makrov. Pravila makrov vam omogočajo, da definirate sintakso po meri, ki se med prevajanjem razširi na kodo Rust. Pravila za makre določajo makre po meri, ki se ujemajo s katerim koli rust izrazom ali stavkom.

Pravila makra boste uporabili za ustvarjanje okvirne kode za abstrahiranje podrobnosti na nizki ravni.

Tukaj je opisano, kako lahko definirate in uporabite pravila makrov v svojih programih Rust:

makro_pravila! make_vector {
( $( $x: izraz ),* ) => {
{
pustitimut v = Vec::novo();
$(
v.push($x);
)*
v
}
};
}

fnglavni() {
pustiti v = make_vector![1, 2, 3];
println!("{:?}", v); // natisne "[1, 2, 3]"
}

Program določa a make_vector! makro, ki ustvari nov vektor iz seznama z vejicami ločenih izrazov v glavni funkcijo.

Znotraj makra se definicija vzorca ujema z argumenti, posredovanimi makru. The $( $x: izraz),* sintaksa se ujema z vsemi z vejicami ločenimi izrazi, identificiranimi kot $x.

The $( ) sintaksa v razširitveni kodi ponovi vsak izraz na seznamu argumentov, posredovanih makru po zaključni oklepaj, ki nakazuje, da se morajo iteracije nadaljevati, dokler makro ne obdela vseh izrazi.

Učinkovito organizirajte svoje projekte Rust

Makri Rust izboljšujejo organizacijo kode, saj vam omogočajo definiranje vzorcev in abstrakcij kode, ki jih je mogoče ponovno uporabiti. Makri vam lahko pomagajo napisati bolj jedrnato, ekspresivno kodo brez podvajanja v različnih delih projekta.

Prav tako lahko organizirate programe Rust v zaboje in module za boljšo organizacijo kode, ponovno uporabnost in medsebojno delovanje z drugimi zaboji in moduli.