Go في المقابلات: 9 فخاخ وكيفية تجنبها

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

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

ابدأ الدورة

مقدمة: لماذا Go ولماذا هو صعب جدًا؟

\n\n

Go (أو Golang) هي لغة يعشقها البعض لبساطتها ويكرهها البعض الآخر لنفس البساطة. في مقابلات Go، نادرًا ما يُطلب منك "كتابة فرز الفقاعات" (على الرغم من أن هذا يحدث أحيانًا). في الغالب، ستواجه مهامًا تتعلق بالتزامن، وفهم بنية الذاكرة، والقدرة على كتابة كود نظيف ومألوف. إذا كنت معتادًا على Python أو Java، فقد تبدو Go صارمة جدًا. لكن هذه الصرامة هي ما يجعلها لغة مثالية لأنظمة الإنتاج.

\n\n

في هذه المقالة، سنحلل الموضوعات الرئيسية التي ستظهر بالتأكيد في المقابلة. لن تحصل على نظرية فقط، بل على أمثلة كود محددة وشرح "تحت الغطاء". هيا بنا!

\n\n

1. الغوروتينات والقنوات: الأساسيات التي يجب أن تحفظها عن ظهر قلب

\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
\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\n
select {\ncase msg := <-ch:\n    fmt.Println(msg)\ncase <-time.After(1 * time.Second):\n    fmt.Println("مهلة زمنية!")\n}\n
\n\n

2. الواجهات: ليست مجرد دوال، بل عقود

\n\n

في Go، يتم تنفيذ الواجهات ضمنيًا. هذا يعني أن الهيكل يفي تلقائيًا بالواجهة إذا كان يحتوي على جميع الدوال المطلوبة. غالبًا ما يُسأل: "ما الفرق بين interface{} و any؟" — الإجابة: لا فرق، هما مترادفان (منذ Go 1.18).

\n\n

مهمة نموذجية في المقابلة

\n

اكتب دالة تأخذ io.Writer وتكتب فيه سلسلة نصية. هذا يختبر فهم الواجهات من المكتبة القياسية.

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

\n\n

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 المضمنة.

\n\n

4. معالجة الأخطاء: لا تتجاهلها

\n\n

في Go لا يوجد try-catch. الأخطاء هي قيم.

المدونات

توصيات الكتب