خبر و ترفند روز

خبر و ترفند های روز را اینجا بخوانید!

Rust Macro: چگونه استفاده از آنها می تواند کد شما را بهبود بخشد

ماکروها به شما امکان می دهند کدی بنویسید که کد دیگری را بنویسد. با دنیای عجیب و قدرتمند فرابرنامه نویسی آشنا شوید.

تولید کد قابلیتی است که در اکثر زبان های برنامه نویسی مدرن پیدا خواهید کرد. این می تواند به شما در کاهش کد و تکرار کد، تعریف زبان های خاص دامنه (DSL) و پیاده سازی نحو جدید کمک کند.

Rust یک سیستم ماکرو قدرتمند را ارائه می دهد که به شما امکان می دهد کد را در زمان کامپایل برای برنامه نویسی پیچیده تر تولید کنید.

مقدمه ای بر Rust Macros

ماکروها نوعی فرابرنامه‌نویسی هستند که می‌توانید از آنها برای نوشتن کدهایی استفاده کنید که کد می‌نویسند. در Rust، ماکرو قطعه ای از کد است که کدهای دیگری را در زمان کامپایل تولید می کند.

ماکروهای Rust یک ویژگی قدرتمند هستند که به شما امکان می‌دهد کدی بنویسید که کد دیگری را در زمان کامپایل برای خودکارسازی کارهای تکراری تولید کند. ماکروهای Rust به کاهش تکرار کد و افزایش قابلیت نگهداری و خوانایی کد کمک می کنند.

می‌توانید از ماکروها برای تولید هر چیزی از کدهای ساده گرفته تا کتابخانه‌ها و چارچوب‌ها استفاده کنید. ماکروها با توابع Rust تفاوت دارند زیرا در زمان اجرا به جای داده ها بر روی کد کار می کنند.

تعریف ماکروها در Rust

ماکروها را با macro_rules تعریف خواهید کرد! کلان. قوانین_کلان! ماکرو یک الگو و یک الگو را به عنوان ورودی می گیرد. Rust الگو را با کد ورودی مطابقت می دهد و از الگو برای تولید کد خروجی استفاده می کند.

در اینجا نحوه تعریف ماکروها در Rust آمده است:

macro_rules! say_hello {
    () => {
        println!("Hello, world!");
    };
}

fn main() {
    say_hello!();
}

کد یک ماکرو say_hello را تعریف می کند که کدی را برای چاپ “Hello, world!” تولید می کند. کد با نحو () با یک ورودی خالی و println مطابقت دارد! ماکرو کد خروجی را تولید می کند.

در اینجا نتیجه اجرای ماکرو در تابع اصلی است:

خروجی برنامه ای که «Hello, world!» را چاپ می کند. به کنسول

ماکروها می توانند آرگومان های ورودی کد تولید شده را دریافت کنند. در اینجا یک ماکرو وجود دارد که یک آرگومان واحد را می گیرد و کدی را برای چاپ یک پیام تولید می کند:

macro_rules! say_message {
    ($message:expr) => {
        println!("{}", $message);
    };
}

ماکرو say_message آرگومان $message را می گیرد و کدی را برای چاپ آرگومان با استفاده از println تولید می کند! کلان. نحو expr با آرگومان در برابر هر عبارت Rust مطابقت دارد.

مطلب مرتبط:   درک نحوه عملکرد Hoisting در جاوا اسکریپت

انواع ماکروهای زنگ

Rust سه نوع ماکرو ارائه می دهد. هر یک از انواع ماکرو اهداف خاصی را دنبال می کنند و نحو و محدودیت های خود را دارند.

ماکروهای رویه ای

ماکروهای رویه ای قوی ترین و همه کاره ترین نوع در نظر گرفته می شوند. ماکروهای رویه ای به شما این امکان را می دهند که سینتکس سفارشی را تعریف کنید که کد Rust را به طور همزمان تولید می کند. می‌توانید از ماکروهای Procedural برای ایجاد ماکروهای مشتق سفارشی، ماکروهای ویژگی‌مانند سفارشی و ماکروهای تابع مانند سفارشی استفاده کنید.

شما از ماکروهای مشتق سفارشی برای پیاده سازی ساختارها و صفات enum به صورت خودکار استفاده خواهید کرد. بسته‌های محبوبی مانند Serde از یک ماکرو مشتق سفارشی برای تولید کد سریال‌سازی و سریال‌زدایی برای ساختارهای داده Rust استفاده می‌کنند.

ماکروهای مشابه ویژگی سفارشی برای افزودن حاشیه نویسی سفارشی به کد Rust مفید هستند. چارچوب وب Rocket از یک ماکرو ویژگی سفارشی برای تعریف مسیرها به طور مختصر و خوانا استفاده می کند.

می توانید از ماکروهای تابع مانند سفارشی برای تعریف عبارات یا عبارات Rust جدید استفاده کنید. جعبه Lazy_static از یک ماکرو تابع مانند سفارشی برای تعریف متغیرهای استاتیک اولیه با تنبلی استفاده می کند.

در اینجا نحوه تعریف یک ماکرو رویه ای که یک ماکرو مشتق سفارشی را تعریف می کند، آمده است:

use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, parse_macro_input};

دستورالعمل های استفاده جعبه ها و انواع لازم را برای نوشتن یک ماکرو رویه Rust وارد می کند.

#[proc_macro_derive(MyTrait)]
pub fn my_derive_macro(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    let gen = quote! {
        impl MyTrait for #name {
            // implementation here
        }
    };

    gen.into()
}

این برنامه یک ماکرو رویه ای را تعریف می کند که اجرای یک صفت را برای یک ساختار یا enum ایجاد می کند. برنامه ماکرو با نام MyTrait را در ویژگی مشتق ساختار یا enum فراخوانی می کند. ماکرو یک شی TokenStream را به عنوان ورودی دریافت می کند که حاوی کد تجزیه شده در یک درخت نحو انتزاعی (AST) با parse_macro_input است! کلان.

مطلب مرتبط:   نحوه یافتن و حذف موارد تکراری در SQL

متغیر نام، ساختار مشتق شده یا شناسه enum، نقل قول است! ماکرو یک AST جدید تولید می کند که نشان دهنده اجرای MyTrait برای نوعی است که در نهایت به عنوان یک TokenStream با متد into برگردانده می شود.

برای استفاده از ماکرو، باید ماکرو را از ماژولی که در آن اعلام کردید وارد کنید:

// assuming you declared the macro in a my_macro_module module

use my_macro_module::my_derive_macro;

هنگام اعلان ساختار یا enum که از ماکرو استفاده می کند، ویژگی #[derive(MyTrait)] را به بالای اعلان اضافه می کنید.

#[derive(MyTrait)]
struct MyStruct {
    // fields here
}

اعلان ساختار با ویژگی به اجرای صفت MyTrait برای ساختار گسترش می یابد:

impl MyTrait for MyStruct {
    // implementation here
}

پیاده سازی به شما امکان می دهد از روش هایی در ویژگی MyTrait در نمونه های MyStruct استفاده کنید.

ماکروها را مشخص کنید

ماکروهای ویژگی، ماکروهایی هستند که می‌توانید آن‌ها را برای موارد Rust مانند ساختارها، enums، توابع و ماژول‌ها اعمال کنید. ماکروهای صفت به شکل یک صفت و به دنبال آن فهرستی از آرگومان ها هستند. ماکرو آرگومان را برای تولید کد Rust تجزیه می کند.

شما از ماکروهای مشخصه برای اضافه کردن رفتارها و حاشیه نویسی های سفارشی به کد خود استفاده خواهید کرد.

در اینجا یک ماکرو ویژگی است که یک ویژگی سفارشی را به ساختار Rust اضافه می کند:

// importing modules for the macro definition
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, AttributeArgs};

#[proc_macro_attribute]
pub fn my_attribute_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
    let args = parse_macro_input!(attr as AttributeArgs);
    let input = parse_macro_input!(item as DeriveInput);
    let name = &input.ident;

    let gen = quote! {
        #input
        impl #name {
            // custom behavior here
        }
    };

    gen.into()
}

ماکرو لیستی از آرگومان ها و یک تعریف ساختار را می گیرد و یک ساختار اصلاح شده با رفتار سفارشی تعریف شده تولید می کند.

ماکرو دو آرگومان را به عنوان ورودی می گیرد: ویژگی اعمال شده به ماکرو (تجزیه شده با ماکرو parse_macro_input!) و آیتم (تجزیه شده با ماکرو parse_macro_input!). ماکرو از نقل قول استفاده می کند! ماکرو برای تولید کد، شامل آیتم ورودی اصلی و یک بلوک impl اضافی که رفتار سفارشی را تعریف می کند.

مطلب مرتبط:   الگوی طراحی Singleton چیست؟

در نهایت، تابع کد تولید شده را به صورت TokenStream با متد into() برمی گرداند.

قوانین کلان

قوانین کلان ساده ترین و منعطف ترین نوع ماکروها هستند. قوانین ماکرو به شما این امکان را می‌دهند که سینتکس سفارشی را تعریف کنید که در زمان کامپایل به کد Rust گسترش می‌یابد. قوانین ماکرو ماکروهای سفارشی را تعریف می کنند که با هر عبارت یا عبارت rust مطابقت دارند.

شما از قوانین کلان برای تولید کد دیگ بخار برای انتزاع جزئیات سطح پایین استفاده خواهید کرد.

در اینجا نحوه تعریف و استفاده از قوانین ماکرو در برنامه های Rust آمده است:

macro_rules! make_vector {
    ( $( $x:expr ),* ) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x);
            )*
            v
        }
    };
}

fn main() {
    let v = make_vector![1, 2, 3];
    println!("{:?}", v); // prints "[1, 2, 3]"
}

برنامه make_vector را تعریف می کند! یک ماکرو که یک بردار جدید از لیستی از عبارات جدا شده با کاما در تابع اصلی ایجاد می کند.

در داخل ماکرو، تعریف الگو با آرگومان های ارسال شده به ماکرو مطابقت دارد. دستور $($x:expr)* با هر عبارت جدا شده با کاما که به عنوان x$ شناسایی شده است مطابقت دارد.

دستور $( ) در کد توسعه، روی هر عبارت در لیست آرگومان‌های ارسال شده به ماکرو پس از پرانتز بسته، تکرار می‌شود، و نشان می‌دهد که تکرارها باید تا زمانی که ماکرو تمام عبارات را پردازش کند، ادامه یابد.

خروجی برنامه ای که "[1، 2، 3]" را در کنسول چاپ می کند

پروژه های Rust خود را به طور موثر سازماندهی کنید

ماکروهای Rust سازماندهی کد را با امکان تعریف الگوها و انتزاعات کد قابل استفاده مجدد بهبود می بخشند. ماکروها می توانند به شما کمک کنند تا کد مختصرتر و رسا و بدون تکرار در بخش های مختلف پروژه بنویسید.

همچنین، می‌توانید برنامه‌های Rust را در جعبه‌ها و ماژول‌ها برای سازماندهی بهتر کد، قابلیت استفاده مجدد و تعامل با سایر جعبه‌ها و ماژول‌ها سازماندهی کنید.