لماذا التعميم
سرعان ما تصادف أنواعًا يجب أن تعمل لأكثر من نوع عنصر. "زوج" من قيمتَين لا يهتمّ ما إذا كانت القيمتان أعدادًا صحيحة أم نصوصًا أم بنية يُعرّفها المستخدم. التعميم يتيح لك كتابة النوع مرة واحدة وإنشاء حالات منه بأي أنواع عناصر يحتاجها المستدعي.
البديل — كتابة IntPair وStringPair وBytePair وغيرها — يصبح مملًّا بسرعة ولا يتركّب. التعميم في Zero هو الأداة القياسية للمهمة.
الدوال العامة
أعلن معاملات النوع بين قوسَين زاويين بين اسم الدالة وقائمة المعاملات:
fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
return Pair { left: left, right: right }
}
T وU أنواع نائبة — المستدعي يُقرّر ما هما.
الاستدعاءات لا تحتاج عادةً إلى كتابتها صراحة؛ يستنتج المترجم من أنواع الوسائط:
let pair = makePair(40, 2_u8)
هنا T يُستنتَج كـ i32 (الافتراضي لقيمة صحيحة حرفية بلا لاحقة) وU كـ u8 (من لاحقة _u8). الربط الناتج pair يأخذ النوع Pair<i32, u8>.
إذا اختار الاستنتاج الأنواع الخاطئة — مثلًا لأن القيم الحرفية ملتبسة — تستطيع تثبيت المعاملات في موقع الاستدعاء:
let pair = makePair<u8, u8>(1, 2)
(صياغة استدعاء الأقواس الزاوية قد تتنوّع بحسب إصدارات Zero؛ راجع التوثيق الحالي للصياغة الدقيقة. السلوك القائم على الاستنتاج أوّلًا هو الجزء المستقرّ.)
بِنى عامة
تأخذ البِنى معاملات النوع بنفس الطريقة:
shape Pair<T, U> {
left: T,
right: U,
}
كل نوع حقل يستطيع ذكر المعاملات. الحالات تُثبّتها:
let intBytes: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hi", right: "there" }
مثال متكامل يجمع بنية عامة ودالة عامة — اضغط Run لرؤية الاستنتاج في العمل:
إعلان بنية عامة واحد، دالة عامة واحدة، وموقع استدعاء بأنواع قوية. لا حشو IntBytePair.
أسماء الأنواع المستعارة
عندما يتكرّر نوع مُعمَّم نفسه مرارًا، أعطه اسمًا بـ type:
type BytePair = Pair<u8, u8>
الآن BytePair قابلة للتبديل مع Pair<u8, u8> في أيّ مكان تستطيع كتابة نوع:
الاسم المستعار مجرّد ميزة تسمية — لا يُنشئ نوعًا مميَّزًا. دالة تأخذ BytePair ستقبل بسعادة قيمة من النوع Pair<u8, u8> (والعكس).
التعميم في المكتبة القياسية
الآلية ذاتها تُشغّل قدرًا كبيرًا من المكتبة القياسية. بعض ما ستراه في شيفرة Zero الحقيقية:
Maybe<T>— قيمة اختيارية، تحمل إمّاTأو لا شيء.Span<T>— شريحة مُستعارة على قيمT.Span<u8>هي العرض التقليدي على مخزن بايتات.ref<T>وmutref<T>— أنواع مرجعية صريحة للحالات التي تحتاج فيها مشاركة بيانات دون نسخ.
لست مضطرًا لتعلّمها كلّها دفعة واحدة. فكرة التعميم هي أن البنية ذاتها تعمل لأيّ نوع عنصر بين يديك.
متى يدفع التعميم ثمنه (ومتى لا يدفع)
استخدم التعميم عندما تجد نفسك تكتب نفس الدالة أو البنية مرّتَين بأنواع عناصر مختلفة. استخدم نوعًا ملموسًا عندما:
- منطق الدالة لا يصلح إلّا لنوع محدّد (مُحلِّل لـ
String، مثلًا). - تريد أن يظهر النوع في رسائل الأخطاء لتسهيل التنقيح.
- خصائص الأداء تعتمد على حجم محدّد في الذاكرة.
تكلفة التعميم حقيقية — ملفات تنفيذية أكبر (كل حالة تُولّد شيفرة جديدة) ووقت ترجمة أبطأ قليلًا. لمعظم شيفرات التطبيقات تلك التكلفة لا تُذكر، لكن من المفيد معرفتها عندما تبني شيفرة مدمجة محكمة يهمّ فيها حجم الملف.
ملاحظة حول القيود
بعض أنظمة التعميم تتيح تقييد معامل النوع ("T يجب أن يدعم =="، "T يجب أن يُنفِّذ Iterator"). قصّة القيود في Zero قبل الإصدار 1.0 لا تزال تتطوّر — الأمثلة في المستودع الرسمي تستخدم التعميم بشكله الخام، دون حدود تفصيلية. مع استقرار اللغة، توقّع وصول صياغة قيود في شكل صغير ومنتظم متّسق مع بقية اللغة. حاليًا، اكتب تعميمات تعمل لأي T تمرّره فعليًا، ودَع المترجم يُخبرك حين لا تكون العملية مدعومة.
التالي: التعدادات Enums
التعميم يتيح لك التعميم على الأنواع. القطعة البنائية التالية في الطرف المقابل — التعدادات، نوع التعداد البسيط في Zero للحالات التي لا تحمل فيها المتغيِّرات بيانات إضافية.
الأسئلة الشائعة
كيف يعمل التعميم في Zero؟
أعلن معاملات النوع بين قوسَين زاويين بعد اسم الدالة أو البنية: fun makePair<T, U>(left: T, right: U) -> Pair<T, U> أو shape Pair<T, U> { left: T, right: U }. يُثبّت المستدعون المعاملات صراحة (Pair<i32, u8>) أو يدعون المترجم يستنتجها من وسائط الاستدعاء.
هل يمكن للبِنى أن تكون عامة في Zero؟
نعم. تستطيع البنية أخذ معاملات نوع بنفس صياغة القوسَين الزاويَين المستخدمة على الدوال: shape Pair<T, U> { left: T, right: U }. كل حقل يستطيع استخدام المعاملات في نوعه. تتكوّن الحالات بكتابة النوع المُعمَّم — Pair<i32, u8> مثلًا.
هل يجب تحديد معاملات النوع عند استدعاء دالة عامة؟
عادةً لا. يستنتجها المترجم من أنواع الوسائط. استدعاء makePair(40, 2_u8) يكفي — تُصبح T هي i32 وU هي u8. تستطيع تثبيت المعاملات صراحة عندما يختار الاستنتاج النوع الخاطئ أو عندما تريد توثيقها في موقع الاستدعاء.
ما هو الاسم المستعار للنوع (type alias) في Zero؟
اسم النوع المستعار اختصار لتعبير نوع أطول. type BytePair = Pair<u8, u8> يتيح لك كتابة BytePair في أي مكان تكتب فيه عادة Pair<u8, u8>. الاسم المستعار ميزة تسمية بحتة — لا يُقدّم نوعًا جديدًا، فقط طريقة أقصر للإشارة إلى نوع موجود.
أين يظهر التعميم في مكتبة Zero القياسية؟
في كل مكان — حيثما يحتاج نوع للاحتفاظ بنوع عنصر اعتباطي أو العمل عليه. Maybe<T> للقيم الاختيارية، Span<u8> لشرائح البايتات، أنواع حاويات مُعمَّمة على نوع عنصرها. الآلية ذاتها للتعميم تتعامل مع الأنواع التي يُعرّفها المستخدم وأنواع المكتبة القياسية.