لماذا تبدو Go بسيطة ولكنها تحطم المبتدئين؟
\n\nGo (أو Golang) هي لغة تم إنشاؤها بهدف واحد: أن يعاني المبرمجون أقل. بناء جملة بسيط، توازي مدمج، ترجمة سريعة - يبدو أنه يمكن تعلمها في أسبوع. ونعم، يمكن تعلمها. لكن "كتابة كود جيد" و"كتابة كود بلغة Go" قصتان مختلفتان.
\n\nغالبًا ما يجلب المبتدئون عادات من Python أو JavaScript أو Java ويحاولون تطبيقها على Go. النتيجة - أخطاء يصعب اكتشافها، ذعر في وقت التشغيل، وشعور بأن "Go معطلة". تنبيه: Go ليست معطلة. إنها فقط تفكر بشكل مختلف.
\n\nفي هذه المقالة، سنحلل 5 من أكثر الأخطاء شيوعًا عند تعلم Go. إذا كنت مبتدئًا - اقرأ بعناية. إذا كنت تكتب بالفعل بلغة Go - تحقق مما إذا كنت لا تزال ترتكبها.
\n\nالخطأ رقم 1: تجاهل الأخطاء (وعادة من Python)
\n\nفي Python أو JavaScript، اعتدت على أن الدالة يمكنها إرجاع نتيجة أو إلقاء استثناء. تقوم بتغليف الاستدعاء في try/catch وتعيش بسلام. في Go، الأمر مختلف تمامًا: لا توجد استثناءات. هناك فقط قيم مرتجعة، واحدة منها هي الخطأ.
\n\nالخطأ الأكثر شيوعًا للمبتدئين هو كتابة result, _ := someFunc() ونسيان الخطأ. أو الأسوأ من ذلك، عدم التحقق منه ببساطة:
// سيء: تم تجاهل الخطأ\nfile, _ := os.Open("config.yaml")\n// إذا لم يكن الملف موجودًا - file == nil، والسطر التالي سينهار مع ذعر\ndefer file.Close()\n\n\nفي Go، الخطأ ليس شيئًا مخيفًا. إنه مجرد قيمة. يجب التحقق منه. دائمًا. بدون استثناءات.
\n\nكيف تفعل ذلك بشكل صحيح؟
\n\n// جيد: نتحقق من الخطأ فورًا\nfile, err := os.Open("config.yaml")\nif err != nil {\n log.Fatalf("فشل فتح الملف: %v", err)\n}\ndefer file.Close()\n\n\nهذا ليس جنون ارتياب. إنه انضباط. تعلمك Go أن تكون دقيقًا. إذا تجاهلت خطأ في الإنتاج - استعد لسقوط الخدمة في الساعة 3 صباحًا، مع صمت تام في السجلات.
\n\nنصيحة: لا تستخدم أبدًا _ للخطأ، إلا إذا كنت متأكدًا بنسبة 100% أنه لن يحدث أبدًا. ومثل هذه المواقف تكاد لا توجد في الكود الحقيقي.
الخطأ رقم 2: الذعر حيثما يكون الخطأ مطلوبًا
\n\nفي Go، هناك آلية panic و recover. غالبًا ما يخلط المبتدئون بينها وبين الاستثناءات ويبدأون في إلقاء الذعر عند أي مشكلة. هذا خطأ.
panic هو توقف طارئ للبرنامج. إنه مخصص للحالات التي يكون فيها استمرار العمل مستحيلًا: على سبيل المثال، إلغاء الإشارة إلى مؤشر فارغ أو تجاوز حدود المصفوفة. استخدام panic لمعالجة منطق الأعمال أمر غير لائق.
// سيء: ذعر بدلاً من خطأ\nfunc divide(a, b int) int {\n if b == 0 {\n panic("القسمة على صفر!")\n }\n return a / b\n}\n\n\nالنهج الصحيح هو إرجاع خطأ:
\n\n// جيد: نعيد خطأ\nfunc divide(a, b int) (int, error) {\n if b == 0 {\n return 0, fmt.Errorf("القسمة على صفر")\n }\n return a / b, nil\n}\n\n\nاستخدم panic فقط للحالات الحرجة حقًا. كل شيء آخر هو أخطاء. Go تمنحك error ليس من أجل لا شيء.
الخطأ رقم 3: العمل غير الصحيح مع goroutines وغياب التزامن
\n\nGoroutines هي ربما أفضل ميزة في Go. لكنها أيضًا الأكثر خطورة للمبتدئين. الصورة النمطية: تشغيل 1000 goroutine، الكتابة إلى متغير مشترك - وتحصل على سباق بيانات (data race).
\n\n// سيء: سباق بيانات\nvar counter int\nfor i := 0; i < 1000; i++ {\n go func() {\n counter++ // غير آمن!\n }()\n}\ntime.Sleep(time.Second)\nfmt.Println(counter) // النتيجة غير متوقعة\n\n\nلماذا هذه مشكلة؟ لأن counter++ ليست عملية ذرية. إنها تتكون من قراءة وزيادة وكتابة. يمكن لـ goroutine قراءة نفس القيمة، وزيادتها