اذهب إلى المحتوى

مقدمة إلى مفهوم الأنواع المعممة Generic Types في لغة Rust


Naser Dakhel

تحتوي كل لغة برمجة على عدد من الأدوات للتعامل مع تكرار المفاهيم بفعالية، وتمثّل الأنواع المعممة generic types في لغة رست هذه الأداة، والتي تتضمن بدائل مجرّدة لأنواع حقيقية concrete أو خاصيات أخرى. يمكننا التعبير عن سلوك الأنواع المعممة أو كيف ترتبط مع أنواع معممة أخرى دون معرفة نوع القيمة التي ستكون بداخلها عند تصريف وتشغيل الشيفرة البرمجية.

يمكن أن تأخذ الدوال بعض الأنواع المعممة معاملات لها بدلًا من أنواع حقيقية، مثل i32 أو String بطريقة مماثلة لما ستكون عليه دالة تأخذ معاملات بقيم غير معروفة لتشغيل الشيفرة البرمجية ذاتها باستخدام عدّة قيم حقيقية. استخدمنا في الحقيقة الأنواع المعممة سابقًا عندما تكلمنا عن Option<T>‎ وفي الفصل الثامن عن Vec<T>‎ و HashMap<K, V>‎ وفي الفصل التاسع عن Result<T, E>‎ في هذه السلسلة البرمجة بلغة رست. سننظر في هذا الفصل إلى كيفية تعريف نوع أو دالة أو تابع خاص بك باستخدام الأنواع المعممة.

دعنا ننظر أولًا إلى كيفية استخراج دالة ما للتقليل من عملية تكرار الشيفرات البرمجية، ثمّ سنستخدم الطريقة ذاتها لإنشاء دالة معمّمة باستخدام دالتين مختلفتين فقط بأنواع معاملاتهما، كما سنشرح أيضًا كيفية استخدام الأنواع المعممة ضمن تعريف هيكل struct أو تعريف مُعدّد enum.

سنتعلّم بعدها كيفية استخدام السمات traits لتعريف السلوك في سياق الأنواع المعممة، إذ يمكنك استخدام السمات مع الأنواع المعممة لتقييد قبول أنواع تحتوي على سلوك معين بدلًا من احتوائها لأي نوع.

وأخيرًا، سنناقش دورات الحياة lifetimes وهي مجموعة من الأنواع المعممة التي تعطي المصرّف معلومات حول ارتباط المراجع references ببعضها بعضًا، وتسمح لنا دورات الحياة بإعطاء المصرف كمًا كافيًا من المعلومات عن القيم المُستعارة borrowed values حتى يتسنّى له التأكد من المراجع التي ستكون صالحة في أكثر من موضع مقارنةً بالمواضع التي يمكن للمصرف التحقق منها بنفسه دون مساعدتنا.

إزالة التكرار باستخراج الدالة

تسمح لنا الأنواع المعممة باستبدال أنواع محددة مع موضع مؤقت placeholder يمثل أنواع عدّة بهدف التخلص من الشيفرة البرمجية المتكررة. دعنا ننظر إلى كيفية التخلص من الشيفرات البرمجية المتكررة دون استخدام الأنواع المعممة قبل أن نتكلم عن كيفية كتابتها، وذلك عن طريق استخراج الدالة التي ستستبدل قيمًا معيّنة بموضع مؤقت يمثّل قيمًا متعددة، ثم نطبق الطريقة ذاتها لاستخراج دالة معممة. سنبدأ بالتعرف على الشيفرات البرمجية المتكررة التي يمكن أن تستخدم الأنواع المعممة عن طريق التعرف على الشيفرات البرمجية المتكررة الممكن استخراجها إلى دالة.

دعنا نبدأ ببرنامج قصير موضّح في الشيفرة 1، إذ يعثر هذا البرنامج على أكبر رقم موجود في قائمة ما.

اسم الملف: src/main.rs

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let mut largest = &number_list[0];

    for number in &number_list {
        if number > largest {
            largest = number;
        }
    }

    println!("The largest number is {}", largest);
}

الشيفرة 1: العثور على أكبر رقم في قائمة من الأرقام

نخزّن هنا قائمة من الأرقام الصحيحة في المتغير number_list ونعيّن مرجعًا إلى العنصر الأول في القائمة ضمن متغير يدعى largest، ثمّ نمرّ على العناصر الموجودة في القائمة بالترتيب ونفحص إذا كان الرقم الحالي أكبر من الرقم الذي خزّننا مرجعه في largest؛ فإذا كانت الإجابة نعم، نستبدل المرجع السابق بمرجع الرقم الحالي؛ وإلا -إذا كان الرقم الحالي أصغر أو تساوي من الرقم largest- لا نغير قيمة المتغير وننتقل إلى الرقم الذي يليه في القائمة. يجب أن يمثل largest مرجعًا لأكبر رقم في القائمة بعد النظر إلى كل الأرقام، وهو في هذه الحالة 100.

تغيّرت مهمتنا الآن: علينا كتابة برنامج يفحص أكبر رقم ضمن قائمتين مختلفتين من الأرقام، ولفعل ذلك يمكننا نسخ الشيفرة البرمجية في الشيفرة 1 مرةً أخرى واستخدام المنطق ذاته في موضعين مختلفين ضمن البرنامج كما هو موضح في الشيفرة 2.

اسم الملف: src/main.rs

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let mut largest = &number_list[0];

    for number in &number_list {
        if number > largest {
            largest = number;
        }
    }

    println!("The largest number is {}", largest);

    let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];

    let mut largest = &number_list[0];

    for number in &number_list {
        if number > largest {
            largest = number;
        }
    }

    println!("The largest number is {}", largest);
}

الشيفرة 2: شيفرة برمجية تجد أكبر رقم في قائمتين من الأرقام

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

لنُنشئ حلًا مجرّدًا للتخلص من التكرار وذلك بتعريف دالة تعمل على أي قائمة من الأرقام الصحيحة بتمريرها مثل معامل للدالة. يجعل هذا الحل من شيفرتنا البرمجية أكثر وضوحًا ويسمح لنا بالتعبير عن مفهوم العثور على أكبر رقم ضمن قائمة ما بصورةٍ مجرّدة.

نستخرج الشيفرة البرمجية التي تبحث عن أكبر عدد إلى دالة تدعى largest في الشيفرة 3، من ثم نستدعي الدالة لإيجاد أكبر عدد في القائمتين الموجودتين في الشيفرة 2، مما يمكننا من استخدام الدالة على أي قائمة تحمل عناصر من النوع i32.

اسم الملف: src/main.rs

fn largest(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];

    let result = largest(&number_list);
    println!("The largest number is {}", result);
}

الشيفرة 3: شيفرة برمجية مجردة لإيجاد أكبر رقم ضمن قائمتين

تقبل الدالة largest معاملًا يدعى list ويمثّل أي شريحة slice حقيقية من قيم i32، ونتيجةً لذلك يمكننا استدعاء الدالة وتنفيذ الشيفرة البرمجية بحسب القيم المحددة التي نمررها للدالة.

اختصارًا لما سبق، إليك الخطوات التي اتبعناها للوصول من الشيفرة 2 إلى الشيفرة 3:

  1. التعرف على الشيفرة البرمجية المتكررة.
  2. استخراج الشيفرة البرمجية المتكررة إلى محتوى دالة وتحديد القيم التي نمررها للدالة والقيم التي تعيدها الدالة في بصمة الدالة signature.
  3. تحديث مواضع نسخ الشيفرة البرمجية لتستدعي الدالة بدلًا من ذلك.

سنستخدم الخطوات ذاتها لاحقًا مع الأنواع المعممة للتقليل من الشيفرات البرمجية المكررة، إذ تسمح الأنواع المعممة للشيفرة البرمجية بالعمل على الأنواع المجردة بالطريقة ذاتها التي يتعامل فيها محتوى الدالة على قائمة مجرّدة بدلًا من قيم محددة.

على سبيل المثال، دعنا نفترض وجود دالتين: دالة تعثر على أكبر رقم في شريحة من قيم i32 وأخرى تعثر على أكبر قيمة في شريحة من قيم char، كيف يمكننا التخلص من التكرار هنا؟ هذا ما سنناقشه تاليًا.

ترجمة -وبتصرف- لقسم من الفصل Generic Types, Traits, and Lifetimes من كتاب The Rust Programming Language.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...