Makro membiarkan Anda menulis kode yang menulis kode lain. Cari tahu tentang dunia metaprogramming yang aneh dan kuat.
Pembuatan kode adalah fitur yang akan Anda temukan di sebagian besar bahasa pemrograman modern. Ini dapat membantu Anda mengurangi kode boilerplate dan duplikasi kode, menentukan bahasa khusus domain (DSL), dan menerapkan sintaks baru.
Rust menyediakan sistem makro yang kuat yang memungkinkan Anda menghasilkan kode pada waktu kompilasi untuk pemrograman yang lebih canggih.
Pengantar Rust Macro
Makro adalah jenis metaprogramming yang dapat Anda manfaatkan untuk menulis kode yang menulis kode. Di Rust, makro adalah potongan kode yang menghasilkan kode lain pada waktu kompilasi.
Makro karat adalah fitur canggih yang memungkinkan Anda menulis kode yang menghasilkan kode lain pada waktu kompilasi untuk mengotomatiskan tugas berulang. Makro Rust membantu mengurangi duplikasi kode dan meningkatkan pemeliharaan dan keterbacaan kode.
Anda dapat menggunakan Macro untuk menghasilkan apa saja mulai dari cuplikan kode sederhana hingga pustaka dan kerangka kerja. Makro berbeda dari
Fungsi karat karena mereka beroperasi pada kode daripada data saat runtime.Mendefinisikan Makro di Rust
Anda akan menentukan makro dengan aturan_makro! makro. Itu aturan_makro! makro mengambil pola dan template sebagai masukan. Rust mencocokkan pola dengan kode masukan dan menggunakan templat untuk menghasilkan kode keluaran.
Inilah cara Anda mendefinisikan Makro di Rust:
aturan_makro! katakan halo {
() => {
cetak!("Halo Dunia!");
};
}
fnutama() {
katakan halo!();
}
Kode mendefinisikan a katakan halo makro yang menghasilkan kode untuk mencetak "Halo, dunia!". Kode cocok dengan () sintaks terhadap masukan kosong dan cetak! makro menghasilkan kode output.
Inilah hasil menjalankan makro di utama fungsi:
Makro dapat mengambil argumen input untuk kode yang dihasilkan. Inilah Makro yang mengambil satu argumen dan menghasilkan kode untuk mencetak pesan:
aturan_makro! say_message {
($pesan: expr) => {
cetak!("{}", $pesan);
};
}
Itu say_message makro mengambil $pesan argumen dan menghasilkan kode untuk mencetak argumen menggunakan cetak! makro. Itu expr sintaks mencocokkan argumen dengan ekspresi Rust apa pun.
Jenis-Jenis Makro Karat
Rust menyediakan tiga jenis makro. Setiap tipe makro melayani tujuan tertentu, dan mereka memiliki sintaks dan batasannya sendiri.
Makro Prosedural
Makro prosedural dianggap sebagai jenis yang paling kuat dan serbaguna. Makro prosedural memungkinkan Anda menentukan sintaks khusus yang menghasilkan kode Rust secara bersamaan. Anda dapat menggunakan makro Prosedural untuk membuat makro turunan kustom, makro mirip atribut kustom, dan makro mirip fungsi kustom.
Anda akan menggunakan makro turunan khusus untuk menerapkan sifat struct dan enum secara otomatis. Paket populer seperti Serde menggunakan makro turunan khusus untuk menghasilkan kode serialisasi dan deserialisasi untuk struktur data Rust.
Makro seperti atribut khusus berguna untuk menambahkan anotasi khusus ke kode Rust. Kerangka kerja web Rocket menggunakan makro seperti atribut khusus untuk menentukan rute secara ringkas dan mudah dibaca.
Anda dapat menggunakan makro seperti fungsi Kustom untuk menentukan ekspresi atau pernyataan Rust baru. Peti Lazy_static menggunakan makro seperti fungsi khusus untuk menentukan malas-diinisialisasi variabel statis.
Inilah cara Anda dapat menentukan makro prosedural yang menentukan makro turunan kustom:
menggunakan proc_macro:: TokenStream;
menggunakan kutipan:: kutipan;
menggunakan syn::{DeriveInput, parse_macro_input};
Itu menggunakan arahan mengimpor peti dan jenis yang diperlukan untuk menulis makro prosedural Rust.
#[proc_macro_derive (Sifat Saya)]
pubfnmy_derive_macro(masukan: TokenStream) -> TokenStream {
membiarkan ast = parse_macro_input!(input sebagai DeriveInput);
membiarkan nama = &ast.ident;membiarkan gen = kutipan! {
impl Sifatku untuk #nama {
// implementasi di sini
}
};
gen.ke()
}
Program mendefinisikan makro prosedural yang menghasilkan implementasi sifat untuk struct atau enum. Program memanggil makro dengan nama Sifatku dalam atribut turunan dari struct atau enum. Makro mengambil a TokenStream objek sebagai masukan yang berisi kode yang diuraikan menjadi Pohon Sintaks Abstrak (AST) dengan parse_macro_input! makro.
Itu nama variabel adalah pengidentifikasi struct atau enum yang diturunkan, the mengutip! Makro menghasilkan AST baru yang mewakili implementasi Sifatku untuk tipe yang akhirnya dikembalikan sebagai a TokenStream dengan ke dalam metode.
Untuk menggunakan makro, Anda harus mengimpor makro dari modul tempat Anda mendeklarasikannya:
// dengan asumsi Anda mendeklarasikan makro dalam modul my_macro_module
menggunakan my_macro_module:: my_derive_macro;
Saat mendeklarasikan struct atau enum yang menggunakan makro, Anda akan menambahkan #[turunan (Sifat Saya)] atribut ke bagian atas deklarasi.
#[turunan (Sifat Saya)]
structStruktur Saya {
// bidang di sini
}
Deklarasi struct dengan atribut diperluas ke implementasi dari Sifatku sifat untuk struct:
impl Sifatku untuk Struktur Saya {
// implementasi di sini
}
Implementasi memungkinkan Anda untuk menggunakan metode di Sifatku sifat pada Struktur Saya contoh.
Makro Atribut
Makro atribut adalah makro yang dapat Anda terapkan pada item Rust seperti struct, enum, fungsi, dan modul. Makro atribut berbentuk atribut diikuti dengan daftar argumen. Makro mem-parsing argumen untuk menghasilkan kode Rust.
Anda akan menggunakan makro atribut untuk menambahkan perilaku dan anotasi khusus ke kode Anda.
Berikut adalah makro atribut yang menambahkan atribut khusus ke struktur Rust:
// mengimpor modul untuk definisi makro
menggunakan proc_macro:: TokenStream;
menggunakan kutipan:: kutipan;
menggunakan syn::{parse_macro_input, DeriveInput, AttributeArgs};#[proc_macro_attribute]
pubfnmy_attribute_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
membiarkan args = parse_macro_input!(attr sebagai AttributeArgs);
membiarkan masukan = parse_macro_input!(item sebagai DeriveInput);
membiarkan nama = &input.ident;membiarkan gen = kutipan! {
#memasukkan
impl #nama {
// perilaku khusus di sini
}
};
gen.ke()
}
Makro mengambil daftar argumen dan definisi struktur dan menghasilkan struct yang dimodifikasi dengan perilaku kustom yang ditentukan.
Makro mengambil dua argumen sebagai masukan: atribut diterapkan ke makro (diuraikan dengan parse_macro_input! makro) dan item (diurai dengan parse_macro_input! makro). Makro menggunakan mengutip! makro untuk menghasilkan kode, termasuk item masukan asli dan tambahan impl blok yang mendefinisikan perilaku kustom.
Terakhir, fungsi mengembalikan kode yang dihasilkan sebagai a TokenStream dengan ke dalam() metode.
Aturan Makro
Aturan makro adalah jenis makro yang paling mudah dan fleksibel. Aturan makro memungkinkan Anda menentukan sintaks khusus yang diperluas ke kode Rust pada waktu kompilasi. Aturan makro menentukan makro khusus yang cocok dengan ekspresi atau pernyataan karat apa pun.
Anda akan menggunakan aturan makro untuk menghasilkan kode boilerplate untuk mengabstraksi detail tingkat rendah.
Inilah cara Anda dapat mendefinisikan dan menggunakan aturan makro dalam program Rust Anda:
aturan_makro! make_vector {
( $( $x: expr ),* ) => {
{
membiarkanmut v = Vec::baru();
$(
v.push($x);
)*
ay
}
};
}
fnutama() {
membiarkan v = make_vector![1, 2, 3];
cetak!("{:?}",v); // mencetak "[1, 2, 3]"
}
Program mendefinisikan a make_vector! makro yang membuat vektor baru dari daftar ekspresi yang dipisahkan koma di utama fungsi.
Di dalam makro, definisi pola cocok dengan argumen yang diteruskan ke makro. Itu $( $x: expr ),* sintaks cocok dengan ekspresi yang dipisahkan koma yang diidentifikasi sebagai $x.
Itu $( ) sintaks dalam kode perluasan mengulangi setiap ekspresi dalam daftar argumen yang diteruskan ke makro setelahnya tanda kurung tutup, menunjukkan bahwa iterasi harus dilanjutkan sampai makro memproses semuanya ekspresi.
Atur Proyek Rust Anda Secara Efisien
Makro karat meningkatkan organisasi kode dengan memungkinkan Anda menentukan pola dan abstraksi kode yang dapat digunakan kembali. Makro dapat membantu Anda menulis kode yang lebih ringkas dan ekspresif tanpa duplikasi di berbagai bagian proyek.
Selain itu, Anda dapat mengatur program Rust ke dalam peti dan modul untuk organisasi kode yang lebih baik, dapat digunakan kembali, dan interoperasi dengan peti dan modul lain.