Макросите ви позволяват да пишете код, който пише друг код. Научете повече за странния и мощен свят на метапрограмирането.
Генерирането на код е функция, която ще намерите в повечето съвременни езици за програмиране. Може да ви помогне да намалите шаблонния код и дублирането на код, да дефинирате специфични за домейн езици (DSL) и да внедрите нов синтаксис.
Rust предоставя мощна макро система, която ви позволява да генерирате код по време на компилиране за по-сложно програмиране.
Въведение в Rust Macros
Макросите са вид метапрограмиране, което можете да използвате, за да пишете код, който пише код. В Rust макросът е част от код, който генерира друг код по време на компилиране.
Макросите Rust са мощна функция, която ви позволява да пишете код, който генерира друг код по време на компилиране за автоматизиране на повтарящи се задачи. Макросите на Rust помагат за намаляване на дублирането на код и увеличават поддържаемостта и четливостта на кода.
Можете да използвате макроси, за да генерирате всичко - от прости кодови фрагменти до библиотеки и рамки. Макросите се различават от
Функции на ръждата защото работят с код, а не с данни по време на изпълнение.Дефиниране на макроси в Rust
Ще дефинирате макроси с macro_rules! макрос. The macro_rules! макросът приема модел и шаблон като вход. Rust съпоставя шаблона с входния код и използва шаблона, за да генерира изходния код.
Ето как можете да дефинирате макроси в Rust:
macro_rules! кажи здравей {
() => {
println!("Здравей свят!");
};
}
fnосновен() {
кажи здравей!();
}
Кодът определя a кажи здравей макрос, който генерира код за отпечатване на „Здравей, свят!“. Кодът съответства на () синтаксис срещу празен вход и println! макрос генерира изходния код.
Ето резултата от изпълнението на макроса в основен функция:
Макросите могат да приемат входни аргументи за генерирания код. Ето един макрос, който приема един аргумент и генерира код за отпечатване на съобщение:
macro_rules! say_message {
($message: израз) => {
println!("{}", $съобщение);
};
}
The say_message макросът взема $ съобщение аргумент и генерира код за отпечатване на аргумента с помощта на println! макрос. The израз синтаксис съвпада с аргумента срещу всеки Rust израз.
Видове Rust макроси
Rust предоставя три вида макроси. Всеки от типовете макроси служи за специфични цели и има свой синтаксис и ограничения.
Процедурни макроси
Процедурните макроси се считат за най-мощния и многофункционален тип. Процедурните макроси ви позволяват да дефинирате персонализиран синтаксис, който генерира Rust код едновременно. Можете да използвате процедурни макроси, за да създадете персонализирани макроси за извличане, персонализирани макроси, подобни на атрибути, и макроси, подобни на персонализирани функции.
Ще използвате персонализирани макроси за извличане, за да внедрявате автоматично структури и характеристики на enum. Популярни пакети като Serde използват персонализиран макрос за извличане, за да генерират код за сериализация и десериализация за структури от данни на Rust.
Макросите, подобни на персонализирани атрибути, са удобни за добавяне на персонализирани анотации към кода на Rust. Уеб рамката на Rocket използва персонализиран макрос, подобен на атрибут, за да дефинира маршрути кратко и четливо.
Можете да използвате макроси, подобни на персонализирани функции, за да дефинирате нови Rust изрази или инструкции. Касата Lazy_static използва персонализиран макрос, подобен на функция, за да дефинира мързеливо инициализирано статични променливи.
Ето как можете да дефинирате процедурен макрос, който дефинира персонализиран макрос за извличане:
използване proc_macro:: TokenStream;
използване цитат:: цитат;
използване syn::{DeriveInput, parse_macro_input};
The използване директиви импортират необходимите каси и типове за писане на процедурен макрос на Rust.
#[proc_macro_derive (MyTrait)]
кръчмаfnmy_derive_macro(въведено: TokenStream) -> TokenStream {
позволявам ast = parse_macro_input!(въвеждане като DeriveInput);
позволявам име = &ast.ident;позволявам gen = цитат! {
импл MyTrait за #име {
// изпълнение тук
}
};
gen.into()
}
Програмата дефинира процедурен макрос, който генерира имплементацията на характеристика за структура или enum. Програмата извиква макроса с името MyTrait в атрибута derive на структурата или enum. Макросът отнема a TokenStream обект като вход, съдържащ кода, анализиран в абстрактно синтактично дърво (AST) с parse_macro_input! макрос.
The име променливата е извлечената структура или идентификатор на enum, the цитирам! Макросът генерира нов AST, представляващ изпълнението на MyTrait за типа, който в крайна сметка се връща като a TokenStream с в метод.
За да използвате макроса, ще трябва да импортирате макроса от модула, в който сте го декларирали:
// ако приемем, че сте декларирали макроса в модул my_macro_module
използване my_macro_module:: my_derive_macro;
При деклариране на структурата или enum, която използва макроса, ще добавите #[извличане (MyTrait)] атрибут в горната част на декларацията.
#[извличане (MyTrait)]
структураMyStruct {
// полета тук
}
Декларацията на struct с атрибута се разширява до имплементация на MyTrait черта за структурата:
импл MyTrait за MyStruct {
// изпълнение тук
}
Реализацията ви позволява да използвате методи в MyTrait черта на MyStruct инстанции.
Макроси на атрибути
Макросите на атрибути са макроси, които можете да приложите към елементи на Rust като структури, enum, функции и модули. Макросите на атрибутите са под формата на атрибут, последван от списък с аргументи. Макросът анализира аргумента, за да генерира Rust код.
Ще използвате макроси на атрибути, за да добавите персонализирани поведения и анотации към вашия код.
Ето един макрос на атрибут, който добавя персонализиран атрибут към структура на Rust:
// импортиране на модули за макро дефиницията
използване proc_macro:: TokenStream;
използване цитат:: цитат;
използване syn::{parse_macro_input, DeriveInput, AttributeArgs};#[proc_macro_attribute]
кръчмаfnmy_attribute_macro(attr: TokenStream, елемент: TokenStream) -> TokenStream {
позволявам args = parse_macro_input!(attr като AttributeArgs);
позволявам вход = parse_macro_input!(елемент като DeriveInput);
позволявам име = &input.ident;позволявам gen = цитат! {
#вход
импл #име {
// персонализирано поведение тук
}
};
gen.into()
}
Макросът приема списък с аргументи и дефиниция на структура и генерира модифицирана структура с дефинираното потребителско поведение.
Макросът приема два аргумента като вход: атрибутът, приложен към макроса (анализиран с parse_macro_input! макрос) и елемента (анализиран с parse_macro_input! макро). Макросът използва цитирам! макрос за генериране на кода, включително оригиналния входен елемент и допълнителен импл блок, който определя персонализираното поведение.
Накрая функцията връща генерирания код като a TokenStream с в() метод.
Макро правила
Правилата за макроси са най-простият и гъвкав тип макроси. Правилата за макроси ви позволяват да дефинирате персонализиран синтаксис, който се разширява до Rust код по време на компилиране. Правилата за макроси дефинират персонализирани макроси, които съответстват на всеки rust израз или изявление.
Ще използвате правила за макроси, за да генерирате шаблонен код, за да абстрахирате детайли от ниско ниво.
Ето как можете да дефинирате и използвате макро правила във вашите програми на Rust:
macro_rules! make_vector {
( $( $x: expr ),* ) => {
{
позволяваммут v = Vec::ново();
$(
v.push($x);
)*
v
}
};
}
fnосновен() {
позволявам v = make_vector![1, 2, 3];
println!("{:?}", v); // отпечатва "[1, 2, 3]"
}
Програмата определя a make_vector! макрос, който създава нов вектор от списък с изрази, разделени със запетаи в основен функция.
Вътре в макроса дефиницията на шаблон съответства на аргументите, предадени на макроса. The $($x: израз),* синтаксис съвпада с всички изрази, разделени със запетая, идентифицирани като $x.
The $( ) синтаксис в разширителния код итерира всеки израз в списъка с аргументи, предадени на макроса след затварящата скоба, показваща, че итерациите трябва да продължат, докато макросът обработи всички изрази.
Организирайте вашите Rust проекти ефективно
Макросите на Rust подобряват организацията на кода, като ви позволяват да дефинирате кодови модели и абстракции за многократна употреба. Макросите могат да ви помогнат да напишете по-сбит, изразителен код без дублиране в различни части на проекта.
Освен това можете да организирате програмите на Rust в каси и модули за по-добра организация на кода, повторно използване и взаимодействие с други каси и модули.