Macro-urile vă permit să scrieți cod care scrie alt cod. Aflați despre lumea ciudată și puternică a metaprogramării.
Generarea codului este o caracteristică pe care o veți găsi în majoritatea limbajelor de programare moderne. Vă poate ajuta să reduceți codul standard și duplicarea codului, să definiți limbaje specifice domeniului (DSL) și să implementați o nouă sintaxă.
Rust oferă un sistem macro puternic care vă permite să generați cod în timpul compilării pentru o programare mai sofisticată.
Introducere în Macro-urile Rust
Macro-urile sunt un tip de metaprogramare pe care o puteți folosi pentru a scrie cod care scrie cod. În Rust, o macrocomandă este o bucată de cod care generează alt cod în timpul compilării.
Macro-urile Rust sunt o caracteristică puternică care vă permite să scrieți cod care generează alt cod în timpul compilării pentru automatizarea sarcinilor repetitive. Macrocomenzile Rust ajută la reducerea dublării codului și la creșterea mentenanței și lizibilitatea codului.
Puteți utiliza macrocomenzi pentru a genera orice, de la simple fragmente de cod la biblioteci și cadre. Macrourile diferă de
Funcții de rugină deoarece funcționează mai degrabă pe cod decât pe date în timpul execuției.Definirea macrocomenzilor în Rust
Veți defini macrocomenzi cu macro_reguli! macro. The macro_reguli! macro ia ca intrare un model și un șablon. Rust potrivește modelul cu codul de intrare și folosește șablonul pentru a genera codul de ieșire.
Iată cum puteți defini macrocomenzi în Rust:
macro_reguli! spune buna {
() => {
println!("Salut Lume!");
};
}
fnprincipal() {
spune buna!();
}
Codul definește a spune buna macrocomandă care generează cod pentru a tipări „Bună, lume!”. Codul se potrivește cu () sintaxă față de o intrare goală și println! macro generează codul de ieșire.
Iată rezultatul rulării macrocomenzii în principal funcţie:
Macro-urile pot lua argumente de intrare pentru codul generat. Iată o macrocomandă care ia un singur argument și generează cod pentru a imprima un mesaj:
macro_reguli! spune_mesaj {
($mesaj: expr) => {
println!("{}", $mesaj);
};
}
The spune_mesaj macro ia $mesaj argument și generează cod pentru a tipări argumentul folosind println! macro. The expr sintaxa se potrivește cu argumentul cu orice expresie Rust.
Tipuri de macro-uri de rugină
Rust oferă trei tipuri de macrocomenzi. Fiecare dintre tipurile de macro-uri servește unor scopuri specifice și au sintaxa și limitările lor.
Macro-uri procedurale
Macro-urile procedurale sunt considerate cel mai puternic și versatil tip. Macrocomenzile procedurale vă permit să definiți sintaxa personalizată care generează cod Rust simultan. Puteți utiliza macrocomenzile procedurale pentru a crea macrocomenzi personalizate derivate, macrocomenzi personalizate asemănătoare atributelor și macrocomenzi personalizate asemănătoare funcțiilor.
Veți folosi macrocomenzi de derivare personalizate pentru a implementa automat structurile și trăsăturile enumerate. Pachetele populare precum Serde folosesc o macrocomandă personalizată pentru a genera cod de serializare și deserializare pentru structurile de date Rust.
Macrocomenzile personalizate asemănătoare atributelor sunt utile pentru adăugarea de adnotări personalizate la codul Rust. Cadrul web Rocket folosește o macrocomandă personalizată asemănătoare atributelor pentru a defini rutele în mod concis și ușor de citit.
Puteți utiliza macrocomenzi asemănătoare funcțiilor personalizate pentru a defini noi expresii sau instrucțiuni Rust. Cutia Lazy_static folosește o macrocomandă personalizată asemănătoare unei funcții pentru a defini leneş-iniţializat variabile statice.
Iată cum puteți defini o macrocomandă procedurală care definește o macrocomandă derivată personalizată:
utilizare proc_macro:: TokenStream;
utilizare citat:: citat;
utilizare syn::{DeriveInput, parse_macro_input};
The utilizare directivele importă lăzile și tipurile necesare pentru scrierea unei macrocomenzi procedurale Rust.
#[proc_macro_derive (MyTrait)]
cârciumăfnmy_derive_macro(intrare: TokenStream) -> TokenStream {
lăsa ast = parse_macro_input!(input la fel de DeriveInput);
lăsa nume = &ast.ident;lăsa gen = citat! {
impl MyTrait pentru #Nume {
// implementare aici
}
};
gen.into()
}
Programul definește o macrocomandă procedurală care generează implementarea unei trăsături pentru o structură sau enumerare. Programul invocă macro-ul cu numele MyTrait în atributul derivă al structurii sau enumerarii. Macro-ul ia a TokenStream obiect ca intrare care conține codul analizat într-un arbore de sintaxă abstractă (AST) cu parse_macro_input! macro.
The Nume variabila este structul derivat sau identificatorul de enumerare, the citat! Macro-ul generează un nou AST reprezentând implementarea MyTrait pentru tipul care este în cele din urmă returnat ca a TokenStream cu în metodă.
Pentru a utiliza macrocomandă, va trebui să importați macrocomandă din modulul în care ați declarat-o:
// presupunând că ați declarat macro într-un modul my_macro_module
utilizare my_macro_module:: my_derive_macro;
La declararea structurii sau enumerarii care utilizează macrocomanda, veți adăuga #[derive (MyTrait)] atribuit în partea de sus a declarației.
#[derive (MyTrait)]
structMyStruct {
// câmpuri aici
}
Declarația struct cu atributul se extinde la o implementare a MyTrait trăsătură pentru structura:
impl MyTrait pentru MyStruct {
// implementare aici
}
Implementarea vă permite să utilizați metode în MyTrait trăsătură pe MyStruct instanțe.
Macrocomenzi cu atribute
Macrocomenzile de atribute sunt macrocomenzi pe care le puteți aplica elementelor Rust, cum ar fi structuri, enumerari, funcții și module. Macrocomenzile de atribute iau forma unui atribut urmat de o listă de argumente. Macrocomanda analizează argumentul pentru a genera codul Rust.
Veți folosi macrocomenzi pentru atribute pentru a adăuga comportamente și adnotări personalizate la codul dvs.
Iată o macrocomandă de atribute care adaugă un atribut personalizat unei structuri Rust:
// import module pentru definiția macro
utilizare proc_macro:: TokenStream;
utilizare citat:: citat;
utilizare syn::{parse_macro_input, DeriveInput, AttributeArgs};#[proc_macro_attribute]
cârciumăfnmy_attribute_macro(attr: TokenStream, element: TokenStream) -> TokenStream {
lăsa args = parse_macro_input!(attr la fel de AttributeArgs);
lăsa input = parse_macro_input!(articol la fel de DeriveInput);
lăsa nume = &input.ident;lăsa gen = citat! {
#intrare
impl #Nume {
// comportament personalizat aici
}
};
gen.into()
}
Macro-ul preia o listă de argumente și o definiție a structurii și generează o structură modificată cu comportamentul personalizat definit.
Macro-ul primește două argumente ca intrare: atributul aplicat macro-ului (parsat cu parse_macro_input! macro) și elementul (analizat cu parse_macro_input! macro). Macro-ul folosește citat! macro pentru a genera codul, inclusiv elementul de intrare original și un suplimentar impl bloc care definește comportamentul personalizat.
În cele din urmă, funcția returnează codul generat ca a TokenStream cu în() metodă.
Reguli macro
Regulile macro sunt cel mai simplu și mai flexibil tip de macrocomenzi. Regulile macro vă permit să definiți o sintaxă personalizată care se extinde la codul Rust în timpul compilării. Regulile de macrocomandă definesc macrocomenzi personalizate care se potrivesc cu orice expresie sau declarație de rugină.
Veți folosi reguli macro pentru a genera cod standard pentru a abstra detaliile de nivel scăzut.
Iată cum puteți defini și utiliza regulile macro în programele dvs. Rust:
macro_reguli! face_vector {
( $( $x: expr ),* ) => {
{
lăsamut v = Vec::nou();
$(
v.push($x);
)*
v
}
};
}
fnprincipal() {
lăsa v = face_vector![1, 2, 3];
println!("{:?}", v); // afișează „[1, 2, 3]”
}
Programul definește a make_vector! o macrocomandă care creează un vector nou dintr-o listă de expresii separate prin virgulă din principal funcţie.
În interiorul macrocomenzii, definiția modelului se potrivește cu argumentele transmise macrocomenzii. The $( $x: expr ),* sintaxa se potrivește cu orice expresii separate prin virgulă identificate ca $x.
The $( ) sintaxa din codul de expansiune iterează peste fiecare expresie din lista de argumente transmise macro-ului după paranteza de închidere, indicând faptul că iterațiile ar trebui să continue până când macro-ul procesează toate expresii.
Organizați-vă eficient proiectele Rust
Macrocomenzile Rust îmbunătățesc organizarea codului, permițându-vă să definiți modele și abstracții de cod reutilizabile. Macro-urile vă pot ajuta să scrieți cod mai concis și mai expresiv, fără dubluri în diferite părți ale proiectului.
De asemenea, puteți organiza programele Rust în lăzi și module pentru o mai bună organizare a codului, reutilizare și interoperare cu alte lăzi și module.