التزامن بلا خوف (Fearless Concurrency)
يعد التعامل مع البرمجة المتزامنة (Concurrent Programming) بأمان وكفاءة أحد أهداف لغة Rust الرئيسية الأخرى. تزداد أهمية البرمجة المتزامنة، التي يتم فيها تنفيذ أجزاء مختلفة من البرنامج بشكل مستقل، والبرمجة المتوازية (Parallel Programming)، التي يتم فيها تنفيذ أجزاء مختلفة من البرنامج في نفس الوقت، مع استفادة المزيد من أجهزة الكمبيوتر من معالجاتها المتعددة. تاريخياً، كانت البرمجة في هذه السياقات صعبة وعرضة للأخطاء، وتأمل Rust في تغيير ذلك.
في البداية، اعتقد فريق Rust أن ضمان سلامة الذاكرة (Memory Safety) ومنع مشاكل التزامن (Concurrency) كانا تحديين منفصلين يجب حلهما بطرق مختلفة. ومع مرور الوقت، اكتشف الفريق أن أنظمة الملكية (Ownership) والأنواع (Type Systems) هي مجموعة قوية من الأدوات للمساعدة في إدارة سلامة الذاكرة ومشاكل Concurrency معاً! من خلال الاستفادة من Ownership والتحقق من الأنواع (Type Checking)، تصبح العديد من أخطاء Concurrency أخطاء في وقت الترجمة (Compile-time Errors) في Rust بدلاً من أخطاء وقت التشغيل (Runtime Errors). لذلك، بدلاً من جعلك تقضي الكثير من الوقت في محاولة إعادة إنتاج الظروف الدقيقة التي يحدث فيها خطأ Concurrency في وقت التشغيل، سيرفض الكود غير الصحيح الترجمة (Compile) وسيقدم خطأ يوضح المشكلة. ونتيجة لذلك، يمكنك إصلاح الكود الخاص بك أثناء العمل عليه بدلاً من إصلاحه بعد شحنه إلى بيئة الإنتاج (Production). لقد أطلقنا على هذا الجانب من Rust اسم التزامن بلا خوف (Fearless Concurrency). يسمح لك Fearless Concurrency بكتابة كود خالٍ من الأخطاء الدقيقة ويسهل إعادة هيكلته (Refactor) دون إدخال أخطاء جديدة.
ملاحظة: من أجل التبسيط، سنشير إلى العديد من المشكلات على أنها متزامنة (Concurrent) بدلاً من أن نكون أكثر دقة بقولنا متزامنة و/أو متوازية (Concurrent and/or Parallel). في هذا الفصل، يرجى استبدال كلمة متزامنة و/أو متوازية ذهنياً كلما استخدمنا كلمة متزامنة. في الفصل التالي، حيث يهم التمييز أكثر، سنكون أكثر تحديداً.
تتسم العديد من اللغات بالتمسك الشديد بالحلول التي تقدمها للتعامل مع المشكلات المتزامنة. على سبيل المثال، تمتلك لغة Erlang وظائف أنيقة للتزامن عبر تمرير الرسائل (Message-passing Concurrency)، ولكن لديها طرق غامضة فقط لمشاركة الحالة (Shared State) بين الخيوط (Threads). يعد دعم مجموعة فرعية فقط من الحلول الممكنة استراتيجية معقولة للغات عالية المستوى لأن اللغة عالية المستوى تعد بفوائد ناتجة عن التخلي عن بعض التحكم مقابل الحصول على تجريدات (Abstractions). ومع ذلك، يُتوقع من اللغات منخفضة المستوى تقديم الحل بأفضل أداء في أي موقف معين وأن يكون لديها تجريدات أقل فوق الأجهزة (Hardware). لذلك، تقدم Rust مجموعة متنوعة من الأدوات لنمذجة المشكلات بأي طريقة مناسبة لموقفك ومتطلباتك.
إليك الموضوعات التي سنغطيها في هذا الفصل:
- كيفية إنشاء خيوط (Threads) لتشغيل قطع متعددة من الكود في نفس الوقت.
- التزامن عبر تمرير الرسائل (Message-passing Concurrency)، حيث ترسل القنوات (Channels) رسائل بين Threads.
- التزامن عبر الحالة المشتركة (Shared-state Concurrency)، حيث تمتلك Threads متعددة إمكانية الوصول إلى قطعة معينة من البيانات.
- سمات (Traits)
SyncوSendالتي توسع ضمانات Concurrency في Rust لتشمل الأنواع المعرفة من قبل المستخدم بالإضافة إلى الأنواع التي توفرها المكتبة القياسية (Standard Library).