البرمجة العامة في Rust: دليلك الشامل للكتابة الاحترافية

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

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

ابدأ الدورة

البرمجة العامة في Rust: لماذا لا يمكنك الاستغناء عنها؟

\n\n

عندما تبدأ في كتابة كود بلغة Rust، قد تشعر أن نظام الأنواع هو عدوك. المترجم ينتقد كل فاصلة، وكلمة Generic تثير الرهبة. لكن في الحقيقة، الـ generics (الأنواع العامة) هي أفضل صديق لك. فهي تسمح لك بكتابة كود مرن وآمن وقابل لإعادة الاستخدام دون التضحية بالأداء.

\n\n

في هذا المقال، سنشرح ما هي البرمجة العامة في Rust، وكيفية استخدامها في الدوال، الهياكل، والـ traits. ستكتشف أن البرمجة العامة ليست سحرًا، بل أداة قوية تجعل كودك أنظف وأسرع.

\n\n

ما هي الـ Generics ولماذا نحتاجها؟

\n\n

تخيل أنك تكتب دالة لإيجاد أكبر عنصر في مصفوفة. بدون الـ generics، ستحتاج لكتابة تطبيق منفصل لكل نوع مثل i32 و f64 و String. هذا يستغرق وقتًا طويلاً وينتهك مبدأ DRY (لا تكرر نفسك).

\n\n

الـ generics تحل هذه المشكلة. فهي تسمح بجعل الأنواع معاملات: تكتب دالة واحدة تعمل مع أي نوع بيانات. المترجم يولد تلقائيًا الإصدارات المناسبة من الكود لكل حالة محددة. هذه العملية تسمى monomorphization.

\n\n

صيغة الـ Generics: من البسيط إلى المعقد

\n\n

1. الدوال العامة

\n\n

لنبدأ بمثال أساسي. لنفترض أننا نريد إنشاء دالة تعيد القيمة التي تمررها (دالة الهوية):

\n\n
fn identity<T>(value: T) -> T {\n    value\n}\n\nfn main() {\n    let n = identity(42);\n    let s = identity("hello");\n    println!("{}, {}", n, s); // 42, hello\n}
\n\n

هنا <T> هو معامل النوع. T يمكن أن يكون أي نوع: رقم، نص، هيكل. عند الاستدعاء، المترجم يخمن النوع المناسب. هذا لا يعمل عن طريق التوزيع الديناميكي (كما في Java أو Python)، بل عن طريق توليد كود متخصص أثناء التجميع. لهذا السبب لا تضحي Rust بالأداء.

\n\n

2. قيود الأنواع (Trait Bounds)

\n\n

لكن ماذا لو أردنا ليس فقط تمرير قيمة، بل مثلاً مقارنة رقمين؟ مجرد <T> لن يعمل، لأن عملية المقارنة غير معرفة لجميع الأنواع. نحتاج لتحديد أن T يجب أن يطبق trait PartialOrd:

\n\n
fn max<T: PartialOrd>(a: T, b: T) -> T {\n    if a > b { a } else { b }\n}\n\nfn main() {\n    println!("{}", max(10, 20));        // 20\n    println!("{}", max(3.14, 2.71));    // 3.14\n}
\n\n

الكتابة T: PartialOrd هي trait bound. تقول للمترجم: "استخدم فقط الأنواع التي تم تعريف ترتيب المقارنة لها". جرب تمرير نصين إلى max وستعمل، لأن String تطبق PartialOrd. أما إذا مررت هيكلًا بدون تطبيق، فستحصل على خطأ تجميع.

\n\n

3. قيود متعددة

\n\n

أحيانًا تحتاج أن يحقق النوع عدة traits في نفس الوقت. مثلاً، أن يكون قابلاً للمقارنة والاستنساخ:

\n\n
fn clone_and_max<T: PartialOrd + Clone>(a: T, b: T) -> T {\n    let a_cloned = a.clone();\n    let b_cloned = b.clone();\n    if a_cloned > b_cloned { a } else { b }\n}
\n\n

الصيغة T: PartialOrd + Clone تعني أن النوع يجب أن يطبق كلا الـ trait. هذا قوي، لكن لا تفرط في استخدامه: الكثير من القيود يعقد القراءة.

\n\n

الـ Generics في الهياكل والتعدادات

\n\n

الهياكل العامة

\n\n

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

\n\n
struct Point<T> {\n    x: T,\n    y: T,\n}\n\nimpl<T> Point<T> {\n    fn new(x:                

المدونات

توصيات الكتب