Enums في Go: الطريقة الاصطلاحية والسحر تحت الغطاء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا لا يقول Go «enum»؟

\n\n

إذا أتيت إلى Go من Python أو Java أو C#، فبالتأكيد ستشعر بصدمة ثقافية خفيفة: في Go لا توجد كلمة مفتاحية enum. لا توجد تعدادات مدمجة مع ترقيم تلقائي، ولا توجد دوال .name() أو .valueOf(). فقط أنت والمترجم و... الثوابت.

\n\n

سؤال «ما هي الطريقة الاصطلاحية لتمثيل enums في Go؟» هو أحد أكثر الأسئلة شيوعًا على Stack Overflow في قسم Golang (آلاف المشاهدات). وهذا ليس من قبيل الصدفة. كل مبتدئ عاجلاً أم آجلاً يصطدم بالحائط: «كيف أصنع enum بطريقة Go-أصيلة؟».

\n\n

في هذه المقالة، لن نقدم إجابة فحسب. بل سننظر تحت الغطاء: كيف يعالج Go الثوابت، ما هو iota ولماذا لا يعتبر المترجم قيمتين مختلفتين من int كنوعين مختلفين. وفي النهاية، ستتعلم كيف تجعل enum آمنًا ومريحًا وجميلًا.

\n\n

المشكلة: لماذا الثوابت البسيطة سيئة؟

\n\n

أول ما يتبادر إلى الذهن هو تعريف عدة ثوابت:

\n\n
const (\n    StatusActive   = 1\n    StatusInactive = 2\n    StatusPending  = 3\n)
\n\n

يبدو أنها تعمل. لكن هناك فارق بسيط: أي دالة تستقبل int ستبتلع القيمة 999 أو -42. لن يقول المترجم كلمة واحدة. هذا ليس تعدادًا - إنها مجرد أرقام بأسماء.

\n\n

يجب أن يقوم enum الحقيقي بما يلي:

\n
    \n
  • تحديد مجموعة القيم المسموح بها؛
  • \n
  • أن يكون آمنًا من حيث النوع (لا يمكن تمرير int حيث يُتوقع حالة)؛
  • \n
  • أن يكون قابلاً للقراءة وسهل التوسيع.
  • \n
\n\n

يحل Go هذه المشكلة بشكل اصطلاحي: من خلال الأنواع المخصصة و iota.

\n\n

الطريقة الاصطلاحية: نوع + iota

\n\n

هذا هو شكل الإجابة القانونية من Stack Overflow نفسه:

\n\n
type 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
\n\n

الآن، الدالة التي تستقبل Status لن تُترجم إذا حاولت تمرير int عادي إليها:

\n\n
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 يفعل ذلك تلقائيًا. هذا ليس مريحًا فقط - إنه اصطلاحي.

\n\n

تحت الغطاء: كيف يرى Go enum

\n\n

دعنا نفهم ما يحدث بالفعل في مرحلة الترجمة.

\n\n

1. iota ليس وقت تشغيل

\n\n

iota غير موجود أثناء تنفيذ البرنامج. إنها أداة معالجة مسبقة (بشكل أكثر دقة، جزء من مترجم الحزمة). في مرحلة الترجمة، يتم استبدال جميع iota بأرقام محددة. في الملف الثنائي الناتج، سترى ببساطة 0 و 1 و 2.

\n\n

يمكنك التحقق من ذلك باستخدام go tool compile أو ببساطة النظر إلى كود التجميع:

\n\n
// الكود المصدري\nconst (\n    A                

المدونات

توصيات الكتب