الرئيسية قاعدة المعرفة البرمجة والمنطق السمات والأنواع العامة في Rust: تجريد بلا كلفة أداء
البرمجة والمنطق

السمات والأنواع العامة في Rust: تجريد بلا كلفة أداء

السمات: تعريف سلوك مشترك

السمات (Traits) تُعرّف سلوكاً مشتركاً يمكن لأنواع مختلفة تطبيقه. فكّر فيها كعقد: أي نوع يُطبّق السمة يضمن وجود هذه الدوال.

#[derive(Debug)]
enum SensorError {
    Timeout,
    Disconnected,
    InvalidReading(f64),
}

// سمة تُعرّف سلوك "القراءة" لأي مستشعر
trait Readable {
    fn read(&self) -> Result<f64, SensorError>;
    fn unit(&self) -> &str;

    // دالة افتراضية — يمكن تجاوزها
    fn display_reading(&self) -> String {
        match self.read() {
            Ok(val) => format!("{:.2} {}", val, self.unit()),
            Err(e) => format!("خطأ: {:?}", e),
        }
    }
}

تطبيق السمات لأنواع مختلفة

كل نوع مستشعر يُطبّق السمة بطريقته الخاصة، لكن الواجهة واحدة.

struct TemperatureSensor {
    id: u32,
    current_temp: f64,
}

struct PressureSensor {
    id: u32,
    current_pressure: f64,
    max_rating: f64,
}

struct VibrationSensor {
    id: u32,
    amplitude: f64,
}

impl Readable for TemperatureSensor {
    fn read(&self) -> Result<f64, SensorError> {
        if self.current_temp > 500.0 {
            Err(SensorError::InvalidReading(self.current_temp))
        } else {
            Ok(self.current_temp)
        }
    }
    fn unit(&self) -> &str { "°C" }
}

impl Readable for PressureSensor {
    fn read(&self) -> Result<f64, SensorError> {
        if self.current_pressure > self.max_rating {
            Err(SensorError::InvalidReading(self.current_pressure))
        } else {
            Ok(self.current_pressure)
        }
    }
    fn unit(&self) -> &str { "bar" }
}

impl Readable for VibrationSensor {
    fn read(&self) -> Result<f64, SensorError> {
        Ok(self.amplitude)
    }
    fn unit(&self) -> &str { "mm/s" }

    // تجاوز الدالة الافتراضية
    fn display_reading(&self) -> String {
        match self.read() {
            Ok(val) if val > 8.0 => format!("⚠ اهتزاز عالٍ: {:.2} {}", val, self.unit()),
            Ok(val) => format!("{:.2} {}", val, self.unit()),
            Err(e) => format!("خطأ: {:?}", e),
        }
    }
}

السمات المُشتقة: Debug و Clone و PartialEq

Rust يمكنها إنشاء تطبيقات تلقائية لسمات شائعة باستخدام #[derive].

// derive يُنشئ التطبيق تلقائياً
#[derive(Debug, Clone, PartialEq)]
struct SensorConfig {
    id: u32,
    name: String,
    min_threshold: f64,
    max_threshold: f64,
    poll_interval_ms: u64,
}

fn main() {
    let config = SensorConfig {
        id: 101,
        name: String::from("حرارة المحرك"),
        min_threshold: 10.0,
        max_threshold: 95.0,
        poll_interval_ms: 1000,
    };

    // Debug: طباعة للتشخيص
    println!("الإعدادات: {:?}", config);

    // Clone: نسخة مستقلة
    let mut backup = config.clone();
    backup.poll_interval_ms = 500;

    // PartialEq: مقارنة
    if config != backup {
        println!("الإعدادات تغيرت — يجب إعادة التهيئة");
    }
}

الأنواع العامة: كتابة دوال لأي نوع

الأنواع العامة (Generics) تتيح كتابة دالة واحدة تعمل مع أنواع متعددة. حدود السمات تضمن أن النوع يملك السلوك المطلوب.

// هذه الدالة تعمل مع أي نوع يُطبّق Readable
fn log_reading<T: Readable>(sensor: &T) {
    match sensor.read() {
        Ok(value) => {
            println!("[سجل] القراءة: {:.2} {}", value, sensor.unit());
        }
        Err(e) => {
            println!("[إنذار] فشل القراءة: {:?}", e);
        }
    }
}

// دالة عامة مع عدة حدود سمات
fn compare_readings<T: Readable, U: Readable>(
    sensor_a: &T,
    sensor_b: &U,
) -> Option<f64> {
    let a = sensor_a.read().ok()?;
    let b = sensor_b.read().ok()?;
    Some((a - b).abs())
}

fn main() {
    let temp = TemperatureSensor { id: 1, current_temp: 75.0 };
    let pressure = PressureSensor { id: 2, current_pressure: 3.2, max_rating: 10.0 };

    // نفس الدالة تعمل مع أنواع مختلفة
    log_reading(&temp);
    log_reading(&pressure);
}

حدود السمات وعبارات where

عند تعدد الحدود، عبارة where تجعل التوقيع أوضح وأسهل للقراءة.

use std::fmt;

// بدون where — يصبح التوقيع طويلاً ومزدحماً
fn process_v1<T: Readable + fmt::Display + Clone>(sensor: &T) {
    println!("{}", sensor);
}

// مع where — أنظف وأوضح
fn process_and_log<T, L>(sensor: &T, logger: &L)
where
    T: Readable + fmt::Display,
    L: fmt::Write,
{
    let reading = sensor.display_reading();
    println!("معالجة: {}", reading);
}

// حدود متعددة في هيكل عام
struct MonitoringStation<T>
where
    T: Readable + Clone,
{
    sensors: Vec<T>,
    name: String,
}

impl<T> MonitoringStation<T>
where
    T: Readable + Clone,
{
    fn new(name: &str) -> Self {
        MonitoringStation {
            sensors: Vec::new(),
            name: String::from(name),
        }
    }

    fn add_sensor(&mut self, sensor: T) {
        self.sensors.push(sensor);
    }

    fn read_all(&self) -> Vec<Result<f64, SensorError>> {
        self.sensors.iter().map(|s| s.read()).collect()
    }
}

impl Trait في توقيعات الدوال

impl Trait صيغة مختصرة لتحديد نوع يُطبّق سمة معينة. مفيدة خاصة في أنواع الإعادة حيث لا يعرف المُستدعي النوع الدقيق.

// في المعاملات: بديل مختصر للأنواع العامة
fn check_sensor(sensor: &impl Readable) -> bool {
    sensor.read().is_ok()
}

// في نوع الإعادة: النوع الدقيق مخفي
fn create_temperature_sensor(id: u32) -> impl Readable {
    TemperatureSensor {
        id,
        current_temp: 25.0,
    }
}

// مفيدة عند إعادة أنواع معقدة مثل المُكررات
fn active_readings(sensors: &[Box<dyn Readable>]) -> Vec<f64> {
    sensors.iter()
        .filter_map(|s| s.read().ok())
        .collect()
}

fn main() {
    // لا نحتاج لمعرفة النوع الدقيق
    let sensor = create_temperature_sensor(301);
    println!("القراءة: {}", sensor.display_reading());
}

مثال عملي: مُسجّل مستشعرات عام

نجمع كل ما تعلمناه لبناء مُسجّل يعمل مع أي نوع مستشعر.

use std::fmt;

trait Readable: fmt::Debug {
    fn read(&self) -> Result<f64, SensorError>;
    fn unit(&self) -> &str;
    fn sensor_id(&self) -> u32;
}

// مُسجّل عام يعمل مع أي مستشعر يُطبّق Readable
struct SensorLogger {
    entries: Vec<LogEntry>,
    max_entries: usize,
}

struct LogEntry {
    sensor_id: u32,
    value: f64,
    unit: String,
    timestamp: String,
}

impl SensorLogger {
    fn new(max_entries: usize) -> Self {
        SensorLogger {
            entries: Vec::new(),
            max_entries,
        }
    }

    // تسجيل قراءة من أي مستشعر
    fn log<T: Readable>(&mut self, sensor: &T, timestamp: &str) {
        match sensor.read() {
            Ok(value) => {
                if self.entries.len() >= self.max_entries {
                    self.entries.remove(0); // حذف الأقدم
                }
                self.entries.push(LogEntry {
                    sensor_id: sensor.sensor_id(),
                    value,
                    unit: String::from(sensor.unit()),
                    timestamp: String::from(timestamp),
                });
            }
            Err(e) => {
                println!("[خطأ] المستشعر {}: {:?}",
                    sensor.sensor_id(), e);
            }
        }
    }

    // تقرير القراءات لمستشعر معين
    fn report_for(&self, sensor_id: u32) {
        println!("--- تقرير المستشعر {} ---", sensor_id);
        for entry in self.entries.iter()
            .filter(|e| e.sensor_id == sensor_id)
        {
            println!("  [{}] {:.2} {}",
                entry.timestamp, entry.value, entry.unit);
        }
    }

    // متوسط القراءات لمستشعر
    fn average_for(&self, sensor_id: u32) -> Option<f64> {
        let readings: Vec<f64> = self.entries.iter()
            .filter(|e| e.sensor_id == sensor_id)
            .map(|e| e.value)
            .collect();

        if readings.is_empty() {
            None
        } else {
            Some(readings.iter().sum::<f64>() / readings.len() as f64)
        }
    }
}

fn main() {
    let mut logger = SensorLogger::new(1000);

    let temp = TemperatureSensor { id: 101, current_temp: 82.5 };
    let pressure = PressureSensor {
        id: 201, current_pressure: 4.8, max_rating: 10.0,
    };
    let vibration = VibrationSensor { id: 301, amplitude: 3.2 };

    // مُسجّل واحد لجميع أنواع المستشعرات
    logger.log(&temp, "2026-04-14 08:00");
    logger.log(&pressure, "2026-04-14 08:00");
    logger.log(&vibration, "2026-04-14 08:00");

    logger.report_for(101);

    if let Some(avg) = logger.average_for(101) {
        println!("متوسط الحرارة: {:.1}°C", avg);
    }
}

الخلاصة

  • السمات تُعرّف سلوكاً مشتركاً يمكن لأنواع مختلفة تطبيقه
  • كل نوع يُطبّق السمة بطريقته الخاصة مع واجهة موحدة
  • derive يُنشئ تطبيقات تلقائية لسمات شائعة مثل Debug و Clone
  • الأنواع العامة تكتب دوالاً وهياكل تعمل مع أي نوع يحقق الشروط
  • حدود السمات تضمن أن النوع العام يملك السلوك المطلوب
  • عبارة where تُنظّم الحدود المتعددة بوضوح
  • impl Trait صيغة مختصرة في المعاملات وأنواع الإعادة
  • الجمع بين السمات والأنواع العامة يُنتج كوداً مرناً وآمناً وقابلاً لإعادة الاستخدام
traits generics trait-bounds impl-trait derive polymorphism السمات الأنواع العامة حدود السمات تعدد الأشكال التجريد إعادة الاستخدام