مقدمة: لماذا Go ولماذا هو صعب جدًا؟
\n\nGo (أو Golang) هي لغة يعشقها البعض لبساطتها ويكرهها البعض الآخر لنفس البساطة. في مقابلات Go، نادرًا ما يُطلب منك "كتابة فرز الفقاعات" (على الرغم من أن هذا يحدث أحيانًا). في الغالب، ستواجه مهامًا تتعلق بالتزامن، وفهم بنية الذاكرة، والقدرة على كتابة كود نظيف ومألوف. إذا كنت معتادًا على Python أو Java، فقد تبدو Go صارمة جدًا. لكن هذه الصرامة هي ما يجعلها لغة مثالية لأنظمة الإنتاج.
\n\nفي هذه المقالة، سنحلل الموضوعات الرئيسية التي ستظهر بالتأكيد في المقابلة. لن تحصل على نظرية فقط، بل على أمثلة كود محددة وشرح "تحت الغطاء". هيا بنا!
\n\n1. الغوروتينات والقنوات: الأساسيات التي يجب أن تحفظها عن ظهر قلب
\n\nالغوروتينات ليست خيوطًا
\nالسؤال الأكثر شيوعًا: "ما الفرق بين الغوروتين وخيط نظام التشغيل؟". الإجابة: الغوروتين خفيف الوزن (مكدس من 2 كيلوبايت)، ويتم إدارته بواسطة جدولة Go، وليس بواسطة النواة. مليون غوروتين أمر طبيعي. مليون خيط سيقتل النظام.
\n\n// مثال: تشغيل 10 غوروتينات\nfor i := 0; i < 10; i++ {\n go func(id int) {\n fmt.Printf("الغوروتين %d يعمل\\n", id)\n }(i)\n}\n// انتظار (طريقة سيئة، فقط للمثال)\ntime.Sleep(time.Second)\n\n\nالقنوات: تزامن بدون ألم
\nالقنوات في Go هي "أنابيب" محددة النوع لنقل البيانات بين الغوروتينات. تذكر ثلاث قواعد:
\n- \n
- الكتابة في قناة مغلقة تسبب ذعرًا. \n
- القراءة من قناة مغلقة تعيد القيمة الصفرية. \n
- القناة غير المخزنة مؤقتًا تحجب الغوروتين حتى يظهر مستقبل. \n
// العمل الصحيح مع القناة\nch := make(chan int, 2) // مخزنة مؤقتًا\n\nch <- 1\nch <- 2\nclose(ch)\n\nfor val := range ch {\n fmt.Println(val) // 1, 2\n}\n\n\nنصيحة للمقابلة: إذا سُئلت عن "select"، أظهر مثالًا مع مهلة زمنية:
\n\nselect {\ncase msg := <-ch:\n fmt.Println(msg)\ncase <-time.After(1 * time.Second):\n fmt.Println("مهلة زمنية!")\n}\n\n\n2. الواجهات: ليست مجرد دوال، بل عقود
\n\nفي Go، يتم تنفيذ الواجهات ضمنيًا. هذا يعني أن الهيكل يفي تلقائيًا بالواجهة إذا كان يحتوي على جميع الدوال المطلوبة. غالبًا ما يُسأل: "ما الفرق بين interface{} و any؟" — الإجابة: لا فرق، هما مترادفان (منذ Go 1.18).
مهمة نموذجية في المقابلة
\nاكتب دالة تأخذ io.Writer وتكتب فيه سلسلة نصية. هذا يختبر فهم الواجهات من المكتبة القياسية.
func WriteHello(w io.Writer) {\n _, err := w.Write([]byte("مرحبًا، Go!"))\n if err != nil {\n log.Fatal(err)\n }\n}\n\n// الاستخدام:\nvar buf bytes.Buffer\nWriteHello(&buf)\nfmt.Println(buf.String()) // مرحبًا، Go!\n\n\nالفخ: جميع الواجهات في Go هي مؤشران (نوع + قيمة). الواجهة الفارغة (interface{}) لا تساوي nil إذا كان بداخلها (*int)(nil). تذكر هذا حتى لا تفشل في سؤال nil != nil.
3. الأدوية العامة: جديدة ولكنها إلزامية بالفعل
\n\nمنذ Go 1.18، أصبحت الأدوية العامة جزءًا من اللغة. في المقابلة، قد يُطلب منك كتابة دالة عامة. على سبيل المثال، دالة تأخذ شريحة من أي نوع وتعيد طولها (أمر تافه) أو تجد القيمة القصوى.
\n\n// دالة عامة للبحث عن القيمة القصوى\nfunc Max[T constraints.Ordered](a, b T) T {\n if a > b {\n return a\n }\n return b\n}\n\n// الاستخدام:\nfmt.Println(Max(10, 20)) // 20\nfmt.Println(Max(3.14, 2.71)) // 3.14\n\n\nلاحظ الحزمة constraints — غالبًا ما تُستخدم في الأسئلة. لكن تذكر: منذ Go 1.21، تم استبدالها بـ cmp.Ordered المضمنة.
4. معالجة الأخطاء: لا تتجاهلها
\n\nفي Go لا يوجد try-catch. الأخطاء هي قيم.