مقدمة: لماذا تعتبر Generics في Go نقلة نوعية؟
لفترة طويلة، ظلت لغة Go (Golang) واحدة من اللغات الحديثة القليلة التي لا تدعم البرمجة العامة (generics). كان المطورون مضطرين لاستخدام interface{} وتحويل الأنواع، مما أدى إلى فقدان أمان الأنواع في مرحلة الترجمة وتضخيم الكود. مع إصدار Go 1.18 (فبراير 2022)، تغير الوضع جذرياً.
Generics (الأدوية الجنسية) هي آلية تسمح بكتابة دوال وهياكل بيانات يمكنها العمل مع أي نوع، مع الحفاظ على التصنيف الصارم للأنواع. في هذه المقالة، سنشرح سبب الحاجة إليها، ونعرض بأمثلة عملية كيفية استخدامها في التطوير اليومي بلغة Go.
لماذا نحتاج Generics في Go؟
قبل ظهور الأدوية الجنسية، كان المطورون يستخدمون interface{} (الواجهة الفارغة) لإنشاء دوال عامة. لنأخذ مثالاً كلاسيكياً - دالة تجد العنصر الأصغر في شريحة:
// بدون أدوية جنسية (Go < 1.18)func MinInt(a, b int) int { if a < b { return a } return b}
func MinFloat(a, b float64) float64 { if a < b { return a } return b}لكل نوع، كان لا بد من كتابة دالة منفصلة. باستخدام interface{}، كان يمكن القيام بذلك:
func MinInterface(a, b interface{}) interface{} { // يتطلب تحويل الأنواع والتحقق في وقت التشغيل // هذا غير آمن وغير فعال}مشاكل هذا النهج واضحة:
- فقدان أمان الأنواع - قد يظهر الخطأ فقط أثناء التشغيل.
- الحاجة لتحويل الأنواع - يصبح الكود ضخماً.
- انخفاض الأداء - العمل مع الواجهات يتطلب عمليات إضافية.
تحل Generics كل هذه المشاكل. فهي تسمح بـ:
- كتابة خوارزميات عامة مرة واحدة.
- الحفاظ على التحقق الصارم من الأنواع في مرحلة الترجمة.
- التخلص من تحويل الأنواع وزيادة الأداء.
أساسيات بناء جملة Generics في Go
تعتمد الأدوية الجنسية في Go على معاملات النوع. بناء الجملة بسيط وبديهي. تُحدد معاملات النوع بين قوسين مربعين بعد اسم الدالة أو النوع.
1. دالة بسيطة مع أدوية جنسية
لنكتب نفس دالة الحد الأدنى، ولكن الآن باستخدام الأدوية الجنسية:
package main
import "fmt"
// Min تُرجع القيمة الدنيا من اثنتينfunc Min[T comparable](a, b T) T { if a < b { return a } return b}
func main() { fmt.Println(Min[int](10, 20)) // 10 fmt.Println(Min[float64](3.14, 2.71)) // 2.71 fmt.Println(Min[string]("abc", "xyz")) // abc}هنا [T comparable] هو معامل النوع. comparable هو قيد (constraint) مدمج يضمن أن النوع يدعم عوامل المقارنة == و !=. لاحظ: لعامل <، يجب استخدام القيد constraints.Ordered (من الحزمة golang.org/x/exp/constraints) أو كتابة قيد خاص بك.
2. القيود (Constraints)
القيود هي واجهات تحدد الطرق والعمليات التي يجب أن تدعمها الأنواع. في Go 1.18+، ظهرت حزمة constraints (موجودة حالياً في golang.org/x/exp)، ولكن يمكنك إنشاء قيودك الخاصة.
import "golang.org/x/exp/constraints"
// MinOrdered يعمل فقط مع الأنواع المرتبةfunc MinOrdered[T constraints.Ordered](a, b T) T { if a < b { return a } return b}قيد مخصص للأنواع التي تدعم الطريقة String():