Makra umožňují psát kód, který zapisuje jiný kód. Zjistěte o podivném a mocném světě metaprogramování.

Generování kódu je funkce, kterou najdete ve většině moderních programovacích jazyků. Může vám pomoci snížit standardní kód a duplikaci kódu, definovat jazyky specifické pro doménu (DSL) a implementovat novou syntaxi.

Rust poskytuje výkonný systém maker, který vám umožňuje generovat kód v době kompilace pro sofistikovanější programování.

Úvod do maker Rust

Makra jsou typem metaprogramování, které můžete využít k psaní kódu, který píše kód. V Rustu je makro část kódu, která generuje jiný kód v době kompilace.

Makra Rust jsou výkonná funkce, která vám umožňuje psát kód, který generuje jiný kód v době kompilace pro automatizaci opakujících se úloh. Rustova makra pomáhají snížit duplikaci kódu a zvýšit jeho udržovatelnost a čitelnost.

Makra můžete použít ke generování čehokoli od jednoduchých úryvků kódu po knihovny a rámce. Makra se liší od Funkce rzi protože za běhu pracují spíše s kódem než s daty.

instagram viewer

Definování maker v Rustu

Makra definujete pomocí makro_pravidla! makro. The makro_pravidla! makro bere jako vstup vzor a šablonu. Rust porovná vzor se vstupním kódem a použije šablonu k vygenerování výstupního kódu.

Zde je návod, jak definovat makra v Rustu:

makro_pravidla! Řekni Ahoj {
() => {
println!("Ahoj světe!");
};
}

fnhlavní() {
Řekni Ahoj!();
}

Kód definuje a Řekni Ahoj makro, které generuje kód pro tisk "Ahoj, světe!". Kód odpovídá () syntaxe proti prázdnému vstupu a println! makro vygeneruje výstupní kód.

Zde je výsledek spuštění makra v hlavní funkce:

Makra mohou převzít vstupní argumenty pro vygenerovaný kód. Zde je makro, které vezme jeden argument a vygeneruje kód pro tisk zprávy:

makro_pravidla! říct_zpráva {
($message: expr) => {
println!("{}", $zpráva);
};
}

The říct_zprávu makro vezme $zpráva argument a vygeneruje kód pro tisk argumentu pomocí println! makro. The expr syntaxe odpovídá argumentu proti jakémukoli výrazu Rust.

Typy maker Rust

Rust poskytuje tři typy maker. Každý z typů maker slouží specifickým účelům a má svou syntaxi a omezení.

Procedurální makra

Procedurální makra jsou považována za nejvýkonnější a nejuniverzálnější typ. Procedurální makra umožňují definovat vlastní syntaxi, která současně generuje kód Rust. Procedurální makra můžete použít k vytváření vlastních maker odvozených, vlastních maker podobných atributům a maker podobných vlastním funkcím.

Vlastní odvozená makra použijete k automatické implementaci struktur a vlastností výčtu. Oblíbené balíčky jako Serde používají vlastní odvozené makro ke generování kódu serializace a deserializace pro datové struktury Rust.

Vlastní makra podobná atributům jsou užitečná pro přidávání vlastních poznámek do kódu Rust. Webový rámec Rocket používá vlastní makro podobné atributům k definování tras stručně a čitelně.

K definování nových výrazů nebo příkazů Rust můžete použít vlastní makra podobná funkcím. Lazy_static bedna používá k definování uživatelské makro podobné funkci líně inicializováno statické proměnné.

Zde je návod, jak můžete definovat procedurální makro, které definuje vlastní odvozené makro:

použití proc_macro:: TokenStream;
použití citovat:: citovat;
použití syn::{DeriveInput, parse_macro_input};

The použití direktivy importují potřebné bedny a typy pro psaní procedurálního makra Rust.

#[proc_macro_derive (MyTrait)]
hospodafnmy_derive_macro(vstup: TokenStream) -> TokenStream {
nechat ast = parse_macro_input! (vstup tak jako DeriveInput);
nechat jméno = &ast.ident;

nechat gen = citát! {
impl MyTrait pro #název {
// implementace zde
}
};

gen.into()
}

Program definuje procedurální makro, které generuje implementaci vlastnosti pro strukturu nebo výčet. Program vyvolá makro s názvem MyTrait v atributu derivace struktury nebo enum. Makro trvá a TokenStream objekt jako vstup obsahující kód analyzovaný do abstraktního syntaxového stromu (AST) s parse_macro_input! makro.

The název proměnná je odvozená struktura nebo identifikátor výčtu, the citát! Makro vygeneruje nový AST představující implementaci MyTrait pro typ, který se nakonec vrátí jako a TokenStream s do metoda.

Chcete-li makro použít, musíte makro importovat z modulu, ve kterém jste jej deklarovali:

// za předpokladu, že jste makro deklarovali v modulu my_macro_module

použití my_macro_module:: my_derive_macro;

Při deklaraci struktury nebo výčtu, který používá makro, přidáte #[derive (MyTrait)] atribut na začátek deklarace.

#[derive (MyTrait)]
strukturovatMyStruct {
// pole zde
}

Deklarace struktury s atributem expanduje na implementaci MyTrait vlastnost pro strukturu:

impl MyTrait pro MyStruct {
// implementace zde
}

Implementace umožňuje používat metody v MyTrait vlastnost na MyStruct instance.

Atribut Makra

Atributová makra jsou makra, která můžete použít na položky Rust, jako jsou struktury, výčty, funkce a moduly. Atributová makra mají podobu atributu následovaného seznamem argumentů. Makro analyzuje argument a vygeneruje kód Rust.

K přidání vlastního chování a poznámek do kódu použijete makra atributů.

Zde je makro atributu, které přidá vlastní atribut do struktury Rust:

// import modulů pro definici makra
použití proc_macro:: TokenStream;
použití citovat:: citovat;
použití syn::{parse_macro_input, DeriveInput, AttributeArgs};

#[proc_macro_attribute]
hospodafnmůj_atribut_makro(attr: TokenStream, položka: TokenStream) -> TokenStream {
nechat args = parse_macro_input!(attr tak jako AttributeArgs);
nechat input = parse_macro_input! (položka tak jako DeriveInput);
nechat jméno = &input.ident;

nechat gen = citát! {
#vstup
impl #název {
// zde vlastní chování
}
};

gen.into()
}

Makro vezme seznam argumentů a definice struktury a vygeneruje upravenou strukturu s definovaným uživatelským chováním.

Makro má jako vstup dva argumenty: atribut použitý na makro (analyzovaný s parse_macro_input! makro) a položku (analyzovanou pomocí parse_macro_input! makro). Makro používá citát! makro pro vygenerování kódu, včetně původní vstupní položky a další impl blok, který definuje vlastní chování.

Nakonec funkce vrátí vygenerovaný kód jako a TokenStream s do() metoda.

Pravidla maker

Pravidla maker jsou nejpřímější a nejflexibilnější typ maker. Pravidla maker umožňují definovat vlastní syntaxi, která se v době kompilace rozšíří na kód Rust. Pravidla maker definují vlastní makra, která odpovídají jakémukoli výrazu nebo výrazu rezu.

Pravidla maker použijete ke generování standardního kódu pro abstraktní detaily nízké úrovně.

Zde je návod, jak můžete definovat a používat pravidla maker ve svých programech Rust:

makro_pravidla! make_vector {
( $( $x: expr ),* ) => {
{
nechatmut v = Vec::Nový();
$(
v.push($x);
)*
proti
}
};
}

fnhlavní() {
nechat v = make_vector![1, 2, 3];
println!("{:?}", v); // vytiskne "[1, 2, 3]"
}

Program definuje a make_vector! makro, které vytvoří nový vektor ze seznamu výrazů oddělených čárkami v hlavní funkce.

Uvnitř makra odpovídá definice vzoru argumentům předaným makru. The $( $x: expr ),* syntaxe odpovídá všem výrazům odděleným čárkami označenými jako x $.

The $( ) syntaxe v kódu rozšíření iteruje přes každý výraz v seznamu argumentů předávaných makru poté koncová závorka označující, že iterace by měly pokračovat, dokud makro nezpracuje všechny výrazy.

Organizujte své projekty Rust efektivně

Makra Rust zlepšují organizaci kódu tím, že umožňují definovat opakovaně použitelné vzory kódu a abstrakce. Makra vám mohou pomoci napsat stručnější a výstižnější kód bez duplikací napříč různými částmi projektu.

Také můžete organizovat programy Rust do beden a modulů pro lepší organizaci kódu, opětovnou použitelnost a spolupráci s jinými bednami a moduly.