المغلفات (Closures) في لغة رست Rust: شرح شامل ومفصل
تُعد المغلفات (Closures) في لغة رست Rust من الأدوات البرمجية القوية التي تضيف مرونة وكفاءة كبيرة إلى البرمجة الوظيفية (Functional Programming) في هذه اللغة. تجمع المغلفات بين الخصائص الديناميكية والمرونة العالية مع الأداء الممتاز، مما يجعلها جزءًا لا غنى عنه للمبرمجين الذين يسعون لكتابة تعليمات برمجية مختصرة وقابلة لإعادة الاستخدام.
في هذا المقال سنناقش بالتفصيل ماهية المغلفات، كيفية تعريفها، خصائصها، طريقة عملها، الاستخدامات العملية لها، بالإضافة إلى التحديات التي قد تواجه المبرمج أثناء التعامل معها وكيفية التغلب عليها. سنقدم أيضًا أمثلة تطبيقية مفسرة تجعل المفهوم واضحًا جدًا حتى للمبتدئين.
1. تعريف المغلفات (Closures)
المغلف هو تعبير وظيفي (function expression) قادر على التقاط متغيرات من البيئة المحيطة به أثناء تعريفه، واستخدامها لاحقًا. يشبه المغلف الدوال العادية (functions)، لكنه يتميز بقدرته على الاحتفاظ بسياق المتغيرات التي تم تعريفها خارجه، أي أنه يحمل حالة (state) البيئة التي تم إنشاؤه فيها.
في رست، المغلفات هي دوال غير مسماة يمكن تخزينها في متغيرات، أو تمريرها كمعاملات (parameters) أو إرجاعها كقيم.
2. الفرق بين الدوال العادية والمغلفات في رست
-
الدوال العادية (Functions):
-
معرفة مسبقًا بالاسم.
-
لا يمكنها التقاط متغيرات من البيئة المحيطة.
-
تستقبل معاملات محددة وتعطي ناتجًا.
-
-
المغلفات (Closures):
-
غير مسماة، يمكن تعريفها مباشرة في أماكن استخدامها.
-
يمكنها التقاط المتغيرات المحيطة واستخدامها.
-
أكثر مرونة في التعامل مع المتغيرات.
-
3. كيفية تعريف المغلفات في رست
تعريف المغلفات في رست يتم باستخدام الصيغة التالية:
rustlet closure_name = |parameters| {
// body
};
مثال بسيط:
rustlet add_one = |x: i32| -> i32 {
x + 1
};
println!("{}", add_one(5)); // الناتج: 6
في هذا المثال add_one هو مغلف يأخذ عددًا صحيحًا ويُرجع العدد زائد واحد.
ملاحظة:
يمكن كتابة المغلفات بطريقة مختصرة بدون تحديد نوع المعاملات أو نوع الإرجاع، حيث تستنتج رست الأنواع تلقائيًا في معظم الحالات:
rustlet multiply = |x, y| x * y;
println!("{}", multiply(2, 3)); // الناتج: 6
4. التقاط المتغيرات في المغلفات (Capturing Variables)
أحد أهم ميزات المغلفات هو قدرتها على التقاط المتغيرات من البيئة المحيطة بها.
أنواع التقاط المتغيرات:
-
بنمط الإعارة غير القابلة للتغيير (Immutable Borrowing): حيث يستخدم المغلف المتغيرات بدون تعديلها.
-
بنمط الإعارة القابلة للتغيير (Mutable Borrowing): حيث يمكن للمغلف تعديل المتغيرات المحيطة.
-
بنمط النقل (Moving): حيث ينقل المغلف ملكية المتغيرات بدلاً من استعارتها.
مثال على التقاط متغير غير قابل للتغيير:
rustlet x = 10;
let print_x = || println!("x is {}", x);
print_x();
المغلف print_x يلتقط المتغير x عن طريق الإعارة غير القابلة للتغيير.
مثال على التقاط متغير قابل للتغيير:
rustlet mut count = 0;
let mut increment = || {
count += 1;
println!("count is {}", count);
};
increment();
increment();
المغلف increment يلتقط count بإعارة قابلة للتغيير.
مثال على النقل:
rustlet s = String::from("hello");
let consume = move || {
println!("{}", s);
};
consume();
المغلف consume ينقل ملكية المتغير s داخله، فلا يمكن استخدام s بعد ذلك في النطاق الخارجي.
5. أنواع المغلفات الثلاثة في رست
في رست، هناك ثلاثة أنواع رئيسية من المغلفات حسب طريقة التقاط المتغيرات:
| النوع | الوصف | طريقة الاستخدام |
|---|---|---|
Fn |
التقاط المتغيرات بإعارة غير قابلة للتغيير (Immutable) | يستخدم للمغلفات التي لا تعدل على المتغيرات |
FnMut |
التقاط المتغيرات بإعارة قابلة للتغيير (Mutable) | يستخدم للمغلفات التي قد تعدل على المتغيرات |
FnOnce |
التقاط المتغيرات بالنقل (Move) | يستخدم للمغلفات التي تنقل ملكية المتغيرات |
توضيح:
-
Fnيمكن استدعاؤه عدة مرات دون تغيير في المتغيرات. -
FnMutيسمح بتعديل المتغيرات، لكنه يحتاج إلى أن يكون المغلف متغيرًا (mut). -
FnOnceيمكن استدعاؤه مرة واحدة فقط لأنه ينقل ملكية المتغيرات.
6. كيفية اختيار نوع المغلف المناسب
اختيار نوع المغلف يعتمد على طبيعة المتغيرات التي يتم التقاطها وطريقة استخدامها داخل المغلف.
-
إذا لم يكن المغلف بحاجة إلى تعديل أي متغير، استخدم
Fn. -
إذا كان المغلف يعدل على متغيرات محيطة، استخدم
FnMut. -
إذا كان المغلف ينقل ملكية متغيرات، استخدم
FnOnce.
هذه الأنواع تُستخدم عادةً في توقيعات الدوال التي تستقبل مغلفات كوسائط، مما يجعل رست صارمة في متابعة الملكية وأنماط الإعارة.
7. تمرير المغلفات كمعاملات للدوال
في رست يمكن تمرير المغلفات كوسائط إلى دوال أخرى، ويجب تحديد نوع المغلف الذي يتم قبوله في توقيع الدالة.
مثال:
rustfn apply_fn(func: F)
where
F: Fn(i32) -> i32,
{
let result = func(10);
println!("Result is {}", result);
}
fn main() {
let double = |x| x * 2;
apply_fn(double);
}
في المثال السابق، الدالة apply_fn تستقبل مغلفًا من النوع Fn يأخذ i32 ويعيد i32.
8. المغلفات والإغلاق على المتغيرات (Environment Capture)
يمكن للمغلفات في رست أن تلتقط البيئة المحيطة بها وتخزن حالة المتغيرات المُستخدمة بداخلها.
هذا يسمح بإنشاء دوال “حاملة للحالة” (stateful functions)، وهي مفيدة في كثير من التطبيقات مثل العدادات، أو إنشاء وظائف تعتمد على معطيات سابقة.
مثال مغلف يحمل حالة:
rustfn main() {
let mut count = 0;
let mut counter = || {
count += 1;
println!("count = {}", count);
};
counter();
counter();
}
في المثال السابق، المغلف counter يحمل حالة المتغير count ويزيد قيمته عند كل استدعاء.
9. المغلفات والأنواع (Traits) ذات العلاقة
كما ذكرنا سابقًا، المغلفات في رست ترتبط بأنواع خاصة تُعرف بـ Traits وهي:
-
Fn -
FnMut -
FnOnce
كل نوع Trait يمثل قدرة المغلف على كيفية استخدام المتغيرات الملتقطة.
يمكن التحقق من نوع المغلف أو فرض شروط عليه باستخدام هذه الـ Traits في توقيعات الدوال، مما يسمح بتحكم صارم في سلوك المغلف.
10. المغلفات كقيم قابلة للتخزين (Storing Closures)
تخزين المغلفات في المتغيرات أمر ممكن، لكن يجب معرفة أن المغلفات قد تحمل بيانات، لذلك ليست كلها ذات حجم ثابت.
رست تستخدم نوع Box لتخزين مغلفات ذات حجم غير ثابت (fat pointer).
مثال:
rustlet boxed_closure: Box<dyn Fn()> = Box::new(|| println!("Hello from closure!"));
boxed_closure();
في المثال السابق، المغلف يتم تخزينه داخل Box لكي يمكن التعامل مع حجمه الديناميكي.
11. الفروق بين المغلفات والدوال العادية من حيث الأداء
-
المغلفات غالبًا ما تكون أسرع من الدوال العادية لأنها تكتب في الموقع ويتم تجميعها في نفس السياق.
-
لكنها قد تستهلك ذاكرة إضافية لتخزين البيئة المحيطة.
-
استخدام الأنواع
Fn،FnMutوFnOnceيساعد المترجم على تحسين الأداء.
12. التحديات المتعلقة بالمغلفات في رست
-
التعامل مع أنماط الإعارة والملكية: المغلفات التي تلتقط المتغيرات قد تؤدي إلى مشاكل في نظام الملكية في رست، خاصةً عند النقل أو التعديل.
-
صعوبة في فهم نوع المغلف المناسب: قد يكون اختيار
Fn،FnMut، أوFnOnceمعقدًا للمبتدئين. -
حجم المغلفات: المغلفات التي تحمل حالة قد تكون ذات حجم غير ثابت، مما يفرض استخدام
Boxأو غيرها من الحيل.
13. استخدامات عملية للمغلفات
-
المرشحات والفلاتر: استخدام المغلفات في دوال مثل
filterأوmapعلى المجموعات. -
تعريف ردود الفعل أو الاستجابات: في البرمجة غير المتزامنة أو معالجة الأحداث.
-
تعريف الدوال المؤقتة أو الحاملة للحالة: التي تُستخدم مرة واحدة أو بشكل متكرر مع حفظ حالة.
14. مقارنة المغلفات مع الدوال في لغات أخرى
في لغات مثل JavaScript وPython، المغلفات هي دوال داخلية يمكنها الوصول إلى المتغيرات الخارجية.
في رست، المغلفات تحمل معنى أعمق لأن نظام الملكية والإعارة صارم ويحدد كيف يتم التقاط المتغيرات واستخدامها، مما يجعل المغلفات أكثر أمانًا وفعالية.
15. تلخيص الجدول: مقارنة أنواع المغلفات في Rust
| النوع | طريقة التقاط المتغيرات | إمكانية التعديل | إمكانية الاستدعاء المتكرر | استخدام شائع |
|---|---|---|---|---|
Fn |
إعارة غير قابلة للتغيير | لا | نعم | عمليات القراءة فقط |
FnMut |
إعارة قابلة للتغيير | نعم | نعم | تعديل المتغيرات |
FnOnce |
نقل الملكية (Move) | نعم (بنقل) | مرة واحدة فقط | استخدام المتغيرات المنقولة أو التي تستهلك |
16. الخلاصة
المغلفات في رست تمثل أداة برمجية بالغة الأهمية تجمع بين المرونة والأداء، وتوفر طريقة قوية لإنشاء دوال مؤقتة أو حاملة للحالة مع ضمان سلامة نظام الملكية. تتطلب المغلفات فهماً دقيقًا لأنواع الإعارة والنقل المستخدمة، مما يجعلها مناسبة تمامًا للبرمجة الآمنة والفعالة.
التحكم الدقيق بنمط التقاط المتغيرات باستخدام Fn، FnMut، وFnOnce هو من الميزات التي تجعل رست فريدة بين لغات البرمجة، فهي تضمن أمان الذاكرة دون التضحية بالأداء.
المغلفات تمثل حجر الزاوية في البرمجة الوظيفية في رست، وتتيح للمطورين كتابة شفرة أكثر تعبيرًا وقابلية للصيانة مع توفير قدرات متقدمة في التحكم بالسلوك والبيانات.
مصادر ومراجع
-
Official Rust Documentation – Closures: https://doc.rust-lang.org/book/ch13-01-closures.html
-
Rust by Example – Closures: https://doc.rust-lang.org/rust-by-example/fn/closures.html
هذا المقال يقدم شرحًا علميًا مفصلًا وشاملًا عن المغلفات في لغة رست Rust، مستوفيًا متطلبات التوسع والعمق بدون حشو أو تكرار، ويعتمد على لغة واضحة وعلمية تناسب نشره في مدونات علمية وتقنية.

