ست عدسات. حين تختلط عليك ميزة جديدة تقابلها بعد هذا المنهج، مرّرها عبر هذه الست قبل أن تحفظ قاعدة نحوية — غالبًا ستجدها امتدادًا لواحدة منها.
١. الأسئلة الثلاثة التي تحكم كل قرار تصميمي
كل قاعدة أمان في Rust جواب على أحد ثلاثة أسئلة، بالترتيب الذي تُطرح به:
- من يملك، ومتى يُحرَّر؟ ← البديهية الأولى (٠١): مالك واحد، Drop حتمي.
- من يقرأ ومن يكتب الآن؟ ← البديهية الثانية (٠٢): مشاركة XOR تعديل.
- متى بالضبط تنتهي كل علاقة استعارة؟ ← الأعمار (٠٥): تسمية صريحة للعلاقات عبر حدود الدوال والبُنى.
أي رسالة خطأ من المترجم تستطيع تصنيفها تحت أحد الثلاثة. حين تُصنّفها صحيحًا، يصبح الإصلاح واضحًا بدل أن يكون تجربة-وخطأ عشوائية.
٢. زيرو-كوست ليست شعارًا — إنها نفس الحركة أربع مرات
في كل مرة رأيتَ فيها Rust "تحلّ مشكلة تجريد"، الحل كان نفس الحركة: لا تمحُ المعلومة وقت التشغيل، بل حوّلها لكود ملموس وقت الترجمة.
| التجريد | ماذا يحدث وقت الترجمة | الناتج وقت التشغيل |
|---|---|---|
enum/match (٠٣) | التاغ يُثبَّت في تخطيط الذاكرة | فحص فعلي، لا وهمي (بعكس erasure TS) |
| generics (٠٤) | monomorphization: نسخة آلة لكل نوع مستعمَل | صفر فحص نوع، صفر قفزة إضافية |
| إغلاقات (٠٦) | نوع فريد بحجم = حجم الالتقاط بالضبط | مؤشر دالة عادي إن لم يلتقط شيئًا |
| منتِجات (٠٦) | سلسلة .filter().map() تُدمَج لحلقة واحدة | صفر تخصيص وسيط |
async/.await (١٠) | آلة حالة (enum) تُبنى كاملة | صفر heap إلا إن طلبتَه، صفر مفسّر |
المبدأ الواحد: "ما لا تستعمله لا تدفع ثمنه؛ وما تستعمله، لا تستطيع أن تكتبه يدويًا بكفاءة أعلى." كل ميزة "متقدمة" في هذا المنهج مثال جديد على نفس الجملة، لا فكرة منفصلة.
٣. طيف الثقة: من يثبت الأمان، ومتى؟
مجاني، ترجمة ثمن صغير، تشغيل لا فحص، أنت المسؤول
──────────────────────────────────────────────────────────────────►
borrow checker → RefCell / Mutex → unsafe
(٠١، ٠٢) (panic عند الخرق، ٠٧) (UB عند الخرق، ٠٩)
نفس الالتزام بالضبط ("لا alias+mutate معًا") يُفرَض في ثلاث نقاط مختلفة على هذا الطيف. اختيار النقطة قرار هندسي واعٍ: كلما تحرّكتَ يمينًا، كسبتَ مرونة (أنماط لا يستطيع المترجم إثباتها ساكنًا) ودفعتَ ثمنًا (فحص وقت تشغيل، أو مسؤولية كاملة بلا أي فحص). لا نقطة "صحيحة" مطلقًا — فقط مناسبة للسياق.
٤. الثلاثية الواحدة، بثلاثة أزياء
كل مرة رأيتَ فيها "ثلاثة أشكال" لشيء ما في اللغة، كانت نفس ثلاثية البديهيتين، لا صدفة تسمية:
- استقبال طريقة:
self/&self/&mut self - التقاط إغلاق:
move(تمليك) / التقاط ضمني بـ&/ التقاط ضمني بـ&mut - تكرار
for:for x in v/for x in &v/for x in &mut v
الثلاثة تسأل نفس السؤال بالضبط: تمليك، أم استعارة قراءة، أم استعارة حصرية؟ حين تقابل بناءً جديدًا يبدو "بثلاثة أوضاع"، اسأل أولًا: أهذه الثلاثية نفسها متنكّرة؟ الجواب غالبًا نعم.
٥. Option / Result / Poll — enum واحد، ثلاث دلالات لـ"لا بعد"
enum Option<T> { Some(T), None } // لا قيمة، ربما أبدًا
enum Result<T, E> { Ok(T), Err(E) } // قيمة، أو سبب فشل محدَّد
enum Poll<T> { Ready(T), Pending } // قيمة، أو "ليس الآن — اسأل لاحقًا"
ثلاثتها نفس الشكل الجبري (فرعان، أحدهما يحمل بيانات). الفرق كله في الدلالة التي يحملها الفرع "السلبي": None نهائي، Err معلوماتي (يحمل سببًا)، Pending مؤقت (يعد بمحاولة لاحقة). حين تصمّم enum خاصًا بك لحالة "قد يفشل أو يتأخر"، ابدأ من هذا القالب واسأل: أيّ الثلاثة دلالتي أقرب؟
٦. ترجمان C → Rust (خريطة سريعة لعقلك القديم)
| كنتَ تكتبه في C | يقابله هنا | الفرق الجوهري |
|---|---|---|
malloc/free يدويًا | ownership + Drop حتمي (٠١) | مُثبَت بالمترجم، لا بذاكرتك |
union بلا تاغ | enum (٠٣) | التاغ حقيقي، القراءة الخاطئة مستحيلة نحويًا |
void* + cast يدوي (qsort) | generics + trait bounds (٠٤) | فحص نوع كامل وقت الترجمة، صفر UB |
| مؤشر دالة خام | Fn/dyn Trait (٠٤، ٠٦) | نفس الفكرة، معبّأة بأمان النوع |
errno / return -1 | Result<T, E> + ? (٠٣) | جزء من التوقيع، لا يُنسى بصمت |
NULL | Option<T> (٠٣) | absence نوع حقيقي، لا كذبة في كل مؤشر |
| مؤشر خام + حساب يدوي | *const/*mut داخل unsafe فقط (٠٩) | نفس القوة، محصورة ومُسمّاة صراحة |
| pthread + mutex يدوي | thread/Arc/Mutex (٠٨) | نفس القدرة، سباقات البيانات مستحيلة نحويًا غالبًا |