الأصناف العامة (Generics) في Rust: لماذا هي ضرورية وكيفية استخدامها

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

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

ابدأ الدورة

مقدمة: ما هي Generics ولماذا نحتاجها في Rust؟



Rust هي لغة برمجة أنظمة تشتهر بأمانها وأدائها ونظام الأنواع الصارم. من الأدوات الرئيسية التي تتيح كتابة كود مرن وقابل لإعادة الاستخدام هي Generics (الأنواع العامة).



تسمح Generics بإنشاء دوال وهياكل وتعدادات وطرق يمكنها العمل مع أنواع بيانات مختلفة دون الحاجة إلى تكرار الكود. بدلاً من كتابة دالة منفصلة لكل من i32 أو f64 أو String، تكتب نسخة عامة واحدة.



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



الجزء الرئيسي: كيف تعمل Generics في Rust



1. الدوال العامة: اكتب مرة واحدة - استخدم لأي نوع



أبسط طريقة للتعرف على Generics هي من خلال الدوال العامة. تخيل أنك بحاجة لكتابة دالة تُرجع القيمة الأكبر من قيمتين. بدلاً من كتابتها لكل نوع على حدة، نستخدم التعميم:



fn largest<T: PartialOrd>(list: &[T]) -> &T {    let mut largest = &list[0];    for item in list {        if item > largest {            largest = item;        }    }    largest}

fn main() { let numbers = vec![34, 50, 25, 100, 65]; let result = largest(&numbers); println!("أكبر رقم: {}", result);

let chars = vec!['y', 'm', 'a', 'q']; let result = largest(&chars); println!("أكبر حرف: {}", result);}


هنا <T: PartialOrd> يعني أن الدالة تعمل مع أي نوع T يدعم المقارنة (trait PartialOrd). هذا هو تقييد trait - مفهوم مهم سيتم شرحه لاحقًا.



2. الهياكل والتعدادات العامة: مرونة في تعريف البيانات



Generics لا تقتصر على الدوال فقط، بل تنطبق أيضًا على هياكل البيانات. المثال الكلاسيكي هو هيكل Point الذي يمكنه تخزين إحداثيات من أنواع مختلفة:



struct Point<T> {    x: T,    y: T,}

fn main() { let integer_point = Point { x: 5, y: 10 }; let float_point = Point { x: 1.0, y: 4.0 }; // let wont_work = Point { x: 5, y: 4.0 }; // خطأ! أنواع مختلفة}


إذا كنت بحاجة لأن يكون x و y من أنواع مختلفة، استخدم عدة معاملات تعميم:



struct Point<T, U> {    x: T,    y: U,}

fn main() { let mixed_point = Point { x: 5, y: 4.0 }; // يعمل!}


نفس الشيء ينطبق على التعدادات. المثال الأكثر شهرة هو Option<T> و Result<T, E>، وهما عامان:



enum Option<T> {    Some(T),    None,}

enum Result<T, E> { Ok(T), Err(E),}


3. الطرق في الهياكل العامة: إضافة المنطق



يمكنك تعريف طرق للهياكل العامة باستخدام كتلة impl. ويمكنك إنشاء طرق تعمل فقط مع أنواع معينة:



struct Point<T> {    x: T,    y: T,}

// طرق لجميع الأنواع Timpl<T> Point<T> { fn x(&self) -> &T { &self.x }}

// طريقة فقط للنوع f64impl Point<f64> { fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() }}

fn main() { let p = Point { x: 3.0, y: 4.0 }; println!("p.x = {}", p.x()); println!("المسافة من نقطة الأصل: {}", p.distance_

المدونات

توصيات الكتب