Makronaredbe vam omogućuju pisanje koda koji piše drugi kod. Saznajte više o čudnom i moćnom svijetu metaprogramiranja.
Generiranje koda značajka je koju ćete pronaći u većini modernih programskih jezika. Može vam pomoći smanjiti standardni kod i dupliciranje koda, definirati jezike specifične za domenu (DSL) i implementirati novu sintaksu.
Rust pruža moćan makro sustav koji vam omogućuje generiranje koda tijekom kompilacije za sofisticiranije programiranje.
Uvod u Rust makronaredbe
Makronaredbe su vrsta metaprogramiranja koje možete iskoristiti za pisanje koda koji piše kod. U Rustu, makronaredba je dio koda koji generira drugi kod tijekom kompajliranja.
Rust makronaredbe su moćna značajka koja vam omogućuje pisanje koda koji generira drugi kod tijekom kompajliranja za automatizaciju zadataka koji se ponavljaju. Rustove makronaredbe pomažu smanjiti dupliciranje koda i povećavaju mogućnost održavanja i čitljivost koda.
Makronaredbe možete koristiti za generiranje bilo čega, od jednostavnih isječaka koda do biblioteka i okvira. Makronaredbe se razlikuju od
Funkcije hrđe jer rade na kodu, a ne na podacima tijekom izvođenja.Definiranje makronaredbi u Rustu
Makronaredbe ćete definirati pomoću makro_pravila! makro. The makro_pravila! makronaredba uzima uzorak i predložak kao ulaz. Rust uspoređuje uzorak s ulaznim kodom i koristi predložak za generiranje izlaznog koda.
Evo kako možete definirati makronaredbe u Rustu:
makro_pravila! reci zdravo {
() => {
println!("Pozdrav svijete!");
};
}
fnglavni() {
reci zdravo!();
}
Kodeks definira a reci zdravo makro koji generira kod za ispis "Hello, world!". Šifra odgovara () sintaksu protiv praznog unosa i println! makro generira izlazni kod.
Evo rezultata pokretanja makronaredbe u glavni funkcija:
Makronaredbe mogu uzeti ulazne argumente za generirani kod. Evo makronaredbe koja uzima jedan argument i generira kod za ispis poruke:
makro_pravila! say_message {
($poruka: izraz) => {
println!("{}", $poruka);
};
}
The reci_poruku makro uzima $poruka argument i generira kod za ispis argumenta pomoću println! makro. The ekspr sintaksa odgovara argumentu protiv bilo kojeg Rust izraza.
Vrste makronaredbi Rust
Rust nudi tri vrste makronaredbi. Svaka vrsta makronaredbe služi određenim svrhama i ima svoju sintaksu i ograničenja.
Proceduralne makronaredbe
Proceduralni makronaredbe smatraju se najmoćnijim i najsvestranijim tipom. Proceduralne makronaredbe omogućuju vam da definirate prilagođenu sintaksu koja istovremeno generira Rust kod. Proceduralne makronaredbe možete koristiti za stvaranje prilagođenih makronaredbi za izvođenje, prilagođenih makronaredbi sličnih atributima i prilagođenih makronaredbi sličnih funkcijama.
Upotrijebit ćete prilagođene makronaredbe za izvođenje za automatsku implementaciju struktura i značajki enuma. Popularni paketi kao što je Serde koriste prilagođenu makronaredbu za generiranje koda za serijalizaciju i deserijalizaciju za strukture podataka Rust.
Prilagođene makronaredbe slične atributima praktične su za dodavanje prilagođenih komentara u Rust kod. Rocket web framework koristi prilagođenu makronaredbu sličnu atributu za definiranje ruta sažeto i čitljivo.
Možete koristiti prilagođene makronaredbe slične funkcijama za definiranje novih Rust izraza ili izjava. Lazy_static sanduk koristi prilagođenu makronaredbu sličnu funkciji za definiranje lijeno inicijalizirano statičke varijable.
Evo kako možete definirati proceduralnu makronaredbu koja definira prilagođenu makronaredbu izvođenja:
koristiti proc_macro:: TokenStream;
koristiti citat:: citat;
koristiti syn::{DeriveInput, parse_macro_input};
The koristiti direktive uvoze potrebne sanduke i vrste za pisanje Rust proceduralnog makronaredbe.
#[proc_macro_derive (MyTrait)]
pubfnmoj_derive_macro(unos: TokenStream) -> TokenStream {
neka ast = parse_macro_input!(unos kao DeriveInput);
neka ime = &ast.ident;neka gen = citat! {
impl Moja osobina za #Ime {
// implementacija ovdje
}
};
gen.into()
}
Program definira proceduralnu makronaredbu koja generira implementaciju značajke za strukturu ili enum. Program poziva makronaredbu s imenom Moja osobina u atributu derive strukture ili enuma. Makro uzima a TokenStream objekt kao ulaz koji sadrži kod raščlanjen u stablo apstraktne sintakse (AST) s parse_macro_input! makro.
The Ime varijabla je izvedena struktura ili enum identifikator, citat! Makro generira novi AST koji predstavlja implementaciju Moja osobina za tip koji se na kraju vraća kao a TokenStream s u metoda.
Da biste koristili makronaredbu, morat ćete uvesti makronaredbu iz modula u kojem ste je deklarirali:
// pod pretpostavkom da ste deklarirali makro u modulu my_macro_module
koristiti moj_makro_modul:: moj_izvođenje_makroa;
Prilikom deklariranja strukture ili enuma koji koristi makronaredbu, dodati ćete #[derive (MyTrait)] atribut na vrhu deklaracije.
#[derive (MyTrait)]
strukturiratiMyStruct {
// polja ovdje
}
Deklaracija strukture s atributom proširuje se na implementaciju Moja osobina osobina za strukturu:
impl Moja osobina za MyStruct {
// implementacija ovdje
}
Implementacija vam omogućuje korištenje metoda u Moja osobina osobina na MyStruct instance.
Makronaredbe atributa
Makronaredbe atributa su makronaredbe koje možete primijeniti na Rust stavke poput struktura, enuma, funkcija i modula. Makronaredbe atributa imaju oblik atributa iza kojeg slijedi popis argumenata. Makro analizira argument za generiranje Rust koda.
Upotrijebit ćete makronaredbe atributa za dodavanje prilagođenih ponašanja i komentara u svoj kod.
Evo makronaredbe atributa koja dodaje prilagođeni atribut Rust strukturi:
// uvoz modula za definiciju makronaredbe
koristiti proc_macro:: TokenStream;
koristiti citat:: citat;
koristiti syn::{parse_macro_input, DeriveInput, AttributeArgs};#[proc_macro_attribute]
pubfnmoj_atribut_makro(attr: TokenStream, item: TokenStream) -> TokenStream {
neka args = parse_macro_input!(attr kao AttributeArgs);
neka unos = parse_macro_input!(stavka kao DeriveInput);
neka naziv = &input.ident;neka gen = citat! {
#ulazni
impl #Ime {
// prilagođeno ponašanje ovdje
}
};
gen.into()
}
Makro uzima popis argumenata i definicija strukture i generira modificiranu strukturu s definiranim prilagođenim ponašanjem.
Makronaredba uzima dva argumenta kao ulaz: atribut primijenjen na makronaredbu (raščlanjen s parse_macro_input! makro) i stavka (raščlanjena s parse_macro_input! makro). Makro koristi citat! makro za generiranje koda, uključujući originalnu ulaznu stavku i dodatnu impl blok koji definira prilagođeno ponašanje.
Na kraju, funkcija vraća generirani kod kao a TokenStream s u() metoda.
Makro pravila
Pravila makronaredbi su najjednostavnija i najfleksibilnija vrsta makronaredbi. Pravila makronaredbi omogućuju definiranje prilagođene sintakse koja se proširuje na Rust kod tijekom kompajliranja. Pravila makronaredbi definiraju prilagođene makronaredbe koje odgovaraju bilo kojem rust izrazu ili izjavi.
Upotrijebit ćete makro pravila za generiranje osnovnog koda za apstrahiranje detalja niske razine.
Evo kako možete definirati i koristiti makro pravila u svojim Rust programima:
makro_pravila! make_vector {
( $( $x: izraz ),* ) => {
{
nekamut v = Vec::novi();
$(
v.push($x);
)*
v
}
};
}
fnglavni() {
neka v = make_vector![1, 2, 3];
println!("{:?}", v); // ispisuje "[1, 2, 3]"
}
Program definira a napravi_vektor! makronaredba koja stvara novi vektor iz popisa izraza odvojenih zarezom u glavni funkcija.
Unutar makronaredbe, definicija uzorka odgovara argumentima proslijeđenim makronaredbi. The $( $x: izraz),* sintaksa odgovara svim izrazima odvojenim zarezom identificiranim kao $x.
The $( ) sintaksa u kodu proširenja ponavlja svaki izraz na popisu argumenata proslijeđenih makronaredbi nakon zatvaranje zagrada, što pokazuje da se iteracije trebaju nastaviti dok makro ne obradi sve izrazi.
Učinkovito organizirajte svoje Rust projekte
Rust makronaredbe poboljšavaju organizaciju koda dopuštajući vam definiranje uzoraka koda i apstrakcija za višekratnu upotrebu. Makronaredbe vam mogu pomoći da napišete koncizniji, izražajniji kod bez dupliciranja u različitim dijelovima projekta.
Također, programe Rust možete organizirati u sanduke i module za bolju organizaciju koda, mogućnost ponovne upotrebe i međuoperativnost s drugim sanducima i modulima.