أنماط التصميم في Rust: تقنيات متقدمة مع أمثلة برمجية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

أنماط التصميم في Rust مع أمثلة: المستوى المتقدم



Rust هي لغة تغير نهج التصميم. غالبًا ما تتطلب الأنماط الكلاسيكية من GoF التكيف بسبب نظام الملكية الصارم، ومدة الحياة، وغياب الوراثة. في هذه المقالة، سنحلل أنماط التصميم المتقدمة التي لا تعمل فقط في Rust، بل تستخدم نقاط قوتها: أمان الذاكرة، والتجريدات ذات التكلفة الصفرية، ونظام الأنواع القوي.



1. نمط Builder مع التحقق في مرحلة التجميع



عادةً ما يتم تنفيذ Builder الكلاسيكي في Rust من خلال هيكل يحتوي على حقول اختيارية. على المستوى المتقدم، يمكننا استخدام نظام الأنواع للتحقق من الحقول الإلزامية في مرحلة التجميع. هذا يستبعد أخطاء وقت التشغيل "نسيت تعيين حقل إلزامي".



التنفيذ من خلال Type State (حالة النوع) يجعل Builder آمنًا وموثقًا ذاتيًا.



// حالات Builder
struct Empty;
struct WithName(String);
struct WithAge(u8);
struct Ready(WithName, WithAge);

struct PersonBuilder<State> {
    state: State,
}

impl PersonBuilder<Empty> {
    pub fn new() -> Self {
        PersonBuilder { state: Empty }
    }
    
    pub fn name(self, name: &str) -> PersonBuilder<WithName> {
        PersonBuilder {
            state: WithName(name.to_string()),
        }
    }
}

impl PersonBuilder<WithName> {
    pub fn age(self, age: u8) -> PersonBuilder<Ready> {
        PersonBuilder {
            state: Ready(self.state, WithAge(age)),
        }
    }
}

impl PersonBuilder<Ready> {
    pub fn build(self) -> Person {
        Person {
            name: self.state.0 .0,
            age: self.state.1 .0,
        }
    }
}

struct Person {
    name: String,
    age: u8,
}

fn main() {
    // يتم التجميع - تم تعيين جميع الحقول
    let person = PersonBuilder::new()
        .name("Alice")
        .age(30)
        .build();
    
    // لن يتم تجميعه: لا يوجد name()
    // let person = PersonBuilder::new().age(30).build();
    
    println!("{} is {} years old", person.name, person.age);
}


الميزة: يتم اكتشاف أخطاء التكوين في مرحلة التجميع، وليس أثناء التنفيذ. هذا قيم بشكل خاص للكائنات المعقدة التي تحتوي على عشرات المعلمات.



2. نمط RAII والمؤشرات الذكية لإدارة الموارد



RAII (Resource Acquisition Is Initialization) ليس مجرد نمط، بل هو فلسفة Rust. يجب أن يتم الاستحواذ على كل مورد (ذاكرة، ملف، مأخذ توصيل، قفل) في المنشئ وتحريره في المدمر. تنفذ Rust هذا من خلال الخاصيتين Drop ونظام الملكية.



مثال متقدم — إنشاء غلاف RAII لـ mutex مع التحرير التلقائي.



use std::ops::Deref;
use std::sync::{Arc, Mutex};

struct Guard<T> {
    data: T,
}

impl<T> Drop for Guard<T> {
    fn drop(&mut self) {
        println!("تم تحرير المورد!");
    }
}

impl<T> Deref for Guard<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

struct ResourceManager<T> {
    resource: Arc<Mutex<T>>,
}

impl<T> ResourceManager<T> {
    fn new(data: T) -> Self {
        ResourceManager {
            resource: Arc::new(Mutex::new(data)),
        }
    }
    
    fn acquire(&self) -> Guard<std::sync::MutexGuard<T>> {
        let guard = self.resource.lock().unwrap();
        Guard { data: guard }
    }
}

fn main() {
    let manager = ResourceManager::new(42);
    {
        let guard = manager.acquire();
        println!("القيمة: {}", *guard);
        // guard يخرج عن النطاق -> يحرر mutex تلقائيًا
    } // يطبع "تم تحرير المورد!"
    
    println!("Mutex متاح مرة أخرى");
}


الفكرة الأساسية: يتم استدعاء المدمر تلقائيًا عند خروج الكائن عن النطاق، مما يضمن تحرير الموارد بشكل صحيح ويمنع تسرب الذاكرة.

المدونات

توصيات الكتب