بدأ كل شيء مع Go 1.18: لماذا الأدوية العامة ليست مجرد ميزة عصرية؟
\n\nلفترة طويلة، كان مجتمع Go يفتخر ببساطة اللغة. كان غياب البرمجة العامة (generics) يعتبر خيارًا واعيًا: "لسنا بحاجة إلى قوالب معقدة، نكتب كودًا مفهومًا". لكن الواقع فرض نفسه. لقد سئم المطورون من نسخ ولصق الدوال لأنواع int و string و float64 - أو استخدام interface{} مع تحويل الأنواع لاحقًا، مما يفقد الأمان في مرحلة التجميع.
كان إصدار Go 1.18 (عام 2022) ثورة. أضافت الأدوية العامة (generics) إلى اللغة معاملات الأنواع (type parameters) و القيود (constraints). الآن يمكن لمبرمجي Go كتابة هياكل بيانات وخوارزميات عامة دون التضحية بالأداء والتصنيف الصارم.
\n\nفي هذه المقالة على pythonlib.ru، سنفهم كيف تعمل generics في Go، ونرى أمثلة حقيقية للكود، ونفهم متى يجب استخدامها ومتى لا. هيا بنا!
\n\nما هي الأدوية العامة في Go بكلمات بسيطة؟
\n\nتخيل أنك تكتب دالة تجد العنصر الأدنى في شريحة. بدون الأدوية العامة، سيكون عليك إنشاء ثلاث نسخ: لـ int و float64 و string. أو استخدام interface{} مع تحويل الأنواع - بطيء وغير آمن.
تسمح لك الأدوية العامة بكتابة دالة واحدة تعمل مع أي نوع يفي بشروط معينة. الصياغة بسيطة: بعد اسم الدالة، بين قوسين مربعين، تُحدد معاملات الأنواع.
\n\nالصياغة: الأقواس المربعة وكلمة any
\n\npackage main\n\nimport "fmt"\n\n// Min — دالة عامة للبحث عن الحد الأدنى\nfunc Min[T any](a, b T) T {\n // خطأ: العامل < غير معرّف لـ any\n // if a < b {\n // return a\n // }\n return b\n}\n\nالكود أعلاه لن يتم تجميعه، لأن any هو واجهة فارغة (مرادف لـ interface{}). لا يعرف المترجم ما إذا كان النوع يدعم العامل <. يجب إضافة قيد (constraint).
قيود الأنواع: constraints
\n\nفي Go، تُحدد القيود من خلال الواجهات. يمكن استخدام القيود المضمنة من حزمة golang.org/x/exp/constraints أو كتابة قيود خاصة بك.
import (\n "fmt"\n "golang.org/x/exp/constraints"\n)\n\n// Min تعمل مع أي نوع مرتب\nfunc Min[T constraints.Ordered](a, b T) T {\n if a < b {\n return a\n }\n return b\n}\n\nfunc main() {\n fmt.Println(Min(10, 20)) // 10\n fmt.Println(Min(3.14, 2.71)) // 2.71\n fmt.Println(Min("apple", "banana")) // "apple"\n}\n\nلاحظ: constraints.Ordered يشمل جميع الأنواع التي تم تعريف العوامل < و <= و > و >= لها - وهي int و float و string.
أمثلة عملية: من البسيط إلى المعقد
\n\nدعونا نرى كيف تبسط الأدوية العامة في Go كتابة الكود الحقيقي.
\n\n1. مكدس عام (Stack)
\n\nبدون الأدوية العامة، كان يجب تخزين []interface{} وإجراء type assertion. مع generics - أنيق وآمن.
package main\n\nimport "fmt"\n\n// Stack — هيكل بيانات عام\ntype Stack[T any] struct {\n items []T\n}\n\nfunc (s *Stack[T]) Push(item T) {\n s.items = append(s.items, item)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n if len(s.items) == 0 {\n var zero T\n return zero, false\n }\n item := s.items[len(s.items)-1]\n s.items = s.items[:len(s.items)-1]\n return item, true\n}\n\nfunc main() {\n intStack := Stack[int]{}\n intStack.Push(1)\n intStack.Push(2)\n val, ok := intStack.Pop()\n fmt.Println(val, ok) // 2 true\n\n strStack := Stack[string]{}\n strStack.Push("hello")\n