مقدمة: "وما هذا المخلوق؟"
\n\nتخيل: أنت تكتب دالة بلغة Go تستقبل interface{}. داخل هذه الدالة، تريد أن تفهم ما هي البيانات التي وصلت إليك. رقم؟ سلسلة نصية؟ أم ربما هيكل مخصص؟ إذا كنت قادماً من Python أو JavaScript، حيث يمكن معرفة النوع في سطر واحد (type() أو typeof)، فستفاجأ قليلاً في Go. Go هي لغة ذات كتابة صارمة، ولا يمكنك ببساطة "لمس" نوع الكائن.
السؤال "How to find the type of an object in Go?" على Stack Overflow حصد عشرات الآلاف من المشاهدات. هذا يعني أنه حتى المطورين ذوي الخبرة يخطئون أحياناً. في هذه المقالة، سنستعرض أهم الأخطاء التي يرتكبها المبتدئون (وليس فقط) عند تحديد النوع في Go، ونوضح كيفية تجنبها. ستتعرف على reflect.TypeOf، وfmt.Sprintf مع %T، وtype assertion، وtype switch.
\n\nالطريقة رقم 1: الأسهل — fmt و %T (ولكن مع خدعة)
\n\nأسرع طريقة لمعرفة النوع هي استخدام حزمة fmt والتنسيق %T. هذا يعمل، لكن الكثيرين ينسون أن fmt ليست عصاً سحرية، بل مجرد غلاف لـ reflect.
package main\n\nimport "fmt"\n\nfunc main() {\n var x interface{} = 42\n fmt.Printf("النوع: %T\\n", x) // النوع: int\n\n var s interface{} = "hello"\n fmt.Printf("النوع: %T\\n", s) // النوع: string\n}\n\n\nالأمر بسيط، أليس كذلك؟ لكن الخطأ هنا: %T يُرجع سلسلة نصية، وليس نوعاً. لا يمكنك تنفيذ أي عمليات عليها سوى عرضها على الشاشة. إذا كنت بحاجة للتحقق مما إذا كان الكائن رقماً، والقيام بشيء ما — فلن يساعدك %T. هذا هو الخطأ الأول: استخدام %T في مكان يتطلب منطقاً شرطياً.
كيفية تجنبه
\nاستخدم %T فقط لتصحيح الأخطاء أو طباعة السجلات. للتحقق من الأنواع في الكود، استخدم type assertion أو type switch (سيأتي ذكرهما لاحقاً).
الطريقة رقم 2: حزمة reflect — قوية ولكنها خطيرة
\n\nحزمة reflect هي مجموعة الأدوات الكاملة للتأمل (Reflection) في Go. الطريقة الأكثر شيوعاً هي reflect.TypeOf().
package main\n\nimport (\n "fmt"\n "reflect"\n)\n\nfunc main() {\n var x interface{} = 42\n t := reflect.TypeOf(x)\n fmt.Println(t) // int\n fmt.Println(t.Name()) // int\n fmt.Println(t.Kind()) // int\n}\n\n\nيبدو أن هذا هو الحل الأمثل. لكن هنا يرتكب المبتدئون الخطأ الثاني: الخلط بين Kind() و Name().
- \n
Name()— يُرجع اسم النوع (مثلint،string،MyStruct). \n Kind()— يُرجع "النوع الأساسي" (مثلint،struct،slice). \n
بالنسبة للأنواع المضمنة، يتطابقان، لكن بالنسبة للأنواع المخصصة — لا. على سبيل المثال، إذا أعلنت type MyInt int، فإن Name() سيعيد MyInt، بينما Kind() سيعيد int. إذا كنت تتحقق فقط من Kind()، فقد تفوتك النوع المخصص.
كيفية تجنبه
\nاسأل نفسك دائماً: "هل يهمني النوع الدقيق أم الفئة فقط؟" إذا كنت بحاجة إلى النوع الدقيق (مثل التسلسل) — استخدم Name(). إذا كنت تحتاج فقط إلى فهم أنه رقم — استخدم Kind().
الطريقة رقم 3: Type Assertion — كلاسيكي (وخطأ الذعر)
\n\nType assertion (تأكيد النوع) هو طريقة "لاستخراج" النوع المحدد من interface{}. يبدو كالتالي:
package main\n\nimport "fmt"\n\nfunc main() {\n var x interface{} = 42\n value := x.(int) // نؤكد أن x هو int\n fmt.Println(value + 10) // 52\n}\n\n\nما الخطأ؟ إذا كان x ليس