مقدمة: لماذا لا يقول Go «enum»؟
\n\nإذا أتيت إلى Go من Python أو Java أو C#، فبالتأكيد ستشعر بصدمة ثقافية خفيفة: في Go لا توجد كلمة مفتاحية enum. لا توجد تعدادات مدمجة مع ترقيم تلقائي، ولا توجد دوال .name() أو .valueOf(). فقط أنت والمترجم و... الثوابت.
سؤال «ما هي الطريقة الاصطلاحية لتمثيل enums في Go؟» هو أحد أكثر الأسئلة شيوعًا على Stack Overflow في قسم Golang (آلاف المشاهدات). وهذا ليس من قبيل الصدفة. كل مبتدئ عاجلاً أم آجلاً يصطدم بالحائط: «كيف أصنع enum بطريقة Go-أصيلة؟».
\n\nفي هذه المقالة، لن نقدم إجابة فحسب. بل سننظر تحت الغطاء: كيف يعالج Go الثوابت، ما هو iota ولماذا لا يعتبر المترجم قيمتين مختلفتين من int كنوعين مختلفين. وفي النهاية، ستتعلم كيف تجعل enum آمنًا ومريحًا وجميلًا.
المشكلة: لماذا الثوابت البسيطة سيئة؟
\n\nأول ما يتبادر إلى الذهن هو تعريف عدة ثوابت:
\n\nconst (\n StatusActive = 1\n StatusInactive = 2\n StatusPending = 3\n)\n\nيبدو أنها تعمل. لكن هناك فارق بسيط: أي دالة تستقبل int ستبتلع القيمة 999 أو -42. لن يقول المترجم كلمة واحدة. هذا ليس تعدادًا - إنها مجرد أرقام بأسماء.
يجب أن يقوم enum الحقيقي بما يلي:
\n- \n
- تحديد مجموعة القيم المسموح بها؛ \n
- أن يكون آمنًا من حيث النوع (لا يمكن تمرير
intحيث يُتوقع حالة)؛ \n - أن يكون قابلاً للقراءة وسهل التوسيع. \n
يحل Go هذه المشكلة بشكل اصطلاحي: من خلال الأنواع المخصصة و iota.
الطريقة الاصطلاحية: نوع + iota
\n\nهذا هو شكل الإجابة القانونية من Stack Overflow نفسه:
\n\ntype Status int\n\nconst (\n StatusActive Status = iota // 0\n StatusInactive // 1\n StatusPending // 2\n StatusDeleted // 3\n)\n\nماذا حدث هنا؟
\n- \n
type Status int— أنشأنا نوعًا مسمى جديدًا. نعم، تحت الغطاء هوint، لكن بالنسبة للمترجمStatusوintشيئان مختلفان. \n iotaهو مولد أعداد صحيحة متسلسلة يعمل فقط داخل كتلةconst. كل سطر جديد يزيد القيمة بمقدار 1. \n
الآن، الدالة التي تستقبل Status لن تُترجم إذا حاولت تمرير int عادي إليها:
func Process(s Status) {\n // ...\n}\n\nfunc main() {\n Process(StatusActive) // OK\n Process(1) // خطأ في الترجمة! Cannot use 1 (type int) as type Status\n}\n\nهذا هو الأمان من حيث النوع - السبب الأول والأهم لاستخدام الأنواع المخصصة للتعداد.
\n\nلماذا iota بدلاً من الترقيم اليدوي؟
\n\nتخيل أنك أضفت حالة جديدة في منتصف القائمة. مع الترقيم اليدوي، سيتعين عليك إعادة كتابة جميع القيم التالية. iota يفعل ذلك تلقائيًا. هذا ليس مريحًا فقط - إنه اصطلاحي.
تحت الغطاء: كيف يرى Go enum
\n\nدعنا نفهم ما يحدث بالفعل في مرحلة الترجمة.
\n\n1. iota ليس وقت تشغيل
\n\niota غير موجود أثناء تنفيذ البرنامج. إنها أداة معالجة مسبقة (بشكل أكثر دقة، جزء من مترجم الحزمة). في مرحلة الترجمة، يتم استبدال جميع iota بأرقام محددة. في الملف الثنائي الناتج، سترى ببساطة 0 و 1 و 2.
يمكنك التحقق من ذلك باستخدام go tool compile أو ببساطة النظر إلى كود التجميع:
// الكود المصدري\nconst (\n A