الـ Functions العامة
جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 27 من 61.
لا تقتصر الأنواع العامة (Generics) على الهياكل (structs)—يمكنك أيضاً كتابة دوال مستقلة تعمل مع أي نوع. هذا مفيد عندما تحتاج إلى دالة مساعدة لا تنتمي إلى هيكل (struct) معين ولكن يجب أن تظل مرنة.
تشبه الصيغة ما رأيته مع الـ structs. قم بالتصريح عن المعلمة العامة (generic parameter) داخل أقواس زاوية بعد اسم الدالة، ثم استخدمها في المعلمات (parameters) ونوع الإرجاع (return type):
fn identity<T>(value: T) -> T {
value
}
تأخذ دالة identity هذه قيمة من أي نوع وتعيدها دون تغيير. يعلن <T> عن النوع العام (generic)، ويقبل value: T هذه القيمة كمعامل (parameter)، ويحدد -> T نوع الإرجاع. عند استدعاء الدالة، يستنتج Rust النوع الملموس (concrete type):
let num = identity(42); // T هي i32
let text = identity("hello"); // T هي &str
يمكنك أيضاً استخدام معلمات عامة (generic parameters) متعددة في الدوال، تماماً كما هو الحال مع الـ structs:
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // تُرجع (i32, &str)
تتيح لك الدوال العامة كتابة منطق قابل لإعادة الاستخدام مرة واحدة وتطبيقه على أنواع بيانات متعددة، مما يقلل من تكرار الكود مع الحفاظ على سلامة الأنواع.
التحدي
سهللنقم ببناء وحدة أدوات (utility module) تحتوي على دوال عامة (generic functions) يمكنها العمل مع أي نوع! ستقوم بإنشاء دوال مستقلة توضح كيف تجعل القوالب العامة (generics) كودك مرناً وقابلاً لإعادة الاستخدام دون الارتباط بهيكل (struct) محدد.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
utils.rs: أنشئ مجموعة من الدوال العامة العامة (public generic utility functions):wrap_in_pair<T>— تأخذ قيمة واحدة وتعيد صفاً (tuple) يحتوي على تلك القيمة مرتين:(value, value). يتطلب هذا أن يكون النوع قابلاً للاستنساخClone، لذا استخدم<T: Clone>swap<T, U>— تأخذ قيمتين من أنواع قد تكون مختلفة وتعيدهما بترتيب معكوس كصف(U, T)
main.rs: استورد وحدة الأدوات الخاصة بك واستعرض عمل هذه الدوال العامة مع أنواع مختلفة. أظهر كيف تتعامل نفس تعريفات الدوال مع الأعداد الصحيحة، والأعداد العشرية، والنصوص بسلاسة.
في ملفك الرئيسي، استعرض دوال الأدوات الخاصة بك من خلال:
- استخدام
wrap_in_pairمع عدد صحيح (المدخل الأول، يتم تحويله إلىi32) وطباعة كلا العنصرين - استخدام
wrap_in_pairمع نص (المدخل الثاني) وطباعة كلا العنصرين - استخدام
swapمع عدد صحيح (المدخل الثالث، يتم تحويله إلىi32) ونص (المدخل الرابع)، ثم طباعة النتيجة المتبادلة
يجب أن يتبع مخرجك هذا التنسيق:
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})على سبيل المثال، مع المدخلات 5، و hello، و 42، و world:
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)لاحظ كيف تعمل wrap_in_pair بشكل متطابق لكل من الأعداد الصحيحة والنصوص، وكيف تتعامل swap مع نوعين مختلفين تماماً—هذه هي مرونة الدوال العامة!
ستتلقى أربعة مدخلات: عدد صحيح، نص، عدد صحيح آخر، ونص آخر.
ورقة مرجعية
تسمح لك الدوال العامة (Generic functions) بكتابة دوال برمجية مساعدة ومستقلة تعمل مع أي نوع من البيانات. قم بالتصريح عن المعامل العام (generic parameter) داخل أقواس زاوية بعد اسم الدالة:
fn identity<T>(value: T) -> T {
value
}
يصرح <T> عن معامل النوع العام، والذي يمكن استخدامه بعد ذلك في معاملات الدالة وأنواع الإرجاع. يستنتج Rust النوع الملموس (concrete type) عند استدعاء الدالة:
let num = identity(42); // T is i32
let text = identity("hello"); // T is &str
يمكنك استخدام معاملات عامة متعددة في دالة واحدة:
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // returns (i32, &str)
عندما يحتاج النوع العام إلى قدرات محددة، استخدم قيود السمات (trait bounds):
fn duplicate<T: Clone>(value: T) -> (T, T) {
(value.clone(), value)
}
جرّب بنفسك
mod utils;
use std::io;
fn main() {
// قراءة المدخلات الأربعة
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: استخدم wrap_in_pair مع num1 واطبع النتيجة
// التنسيق: Pair of ints: ({value}, {value})
// TODO: استخدم wrap_in_pair مع str1 واطبع النتيجة
// التنسيق: Pair of strings: ({value}, {value})
// TODO: استخدم swap مع num2 و str2، ثم اطبع النتيجة المبدلة
// التنسيق: Swapped: ({string}, {int})
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط