الانتقال إلى المحتوى الرئيسي
الوحدة 10 · الدرس 3 من 548/50 في الدورة~15 min
دروس الوحدة (3/5)

الأدوية العامة (الإصدار 1.18+)

منذ Go 1.18 (مارس 2022)، معلمات النوع متاحة: يمكن تحديد معلمات الوظائف والأنواع على الأنواع. إنها تتيح لك كتابة Map، Filter، Set[T]، LinkedList[T] بدون تكرار التعليمات البرمجية وبدون interface{} + تأكيدات نوع وقت التشغيل.

الوظيفة العامة: النحو

Go
func Map[T, U any](s []T, f func(T) U) []U {
    out := make([]U, len(s))
    for i, v := range s {
        out[i] = f(v)
    }
    return out
}

doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// doubled is []int{2, 4, 6}

ثلاثة عناصر جديدة:

  • [T, U any] بعد الاسم: يعلن عن معلمات النوع مع قيودها.
  • any = قيد يقبل أي نوع (الاسم المستعار لـ interface{} منذ Go 1.18).
  • الاستدلال: الاتصال بـ Map([]int{...}, ...) ليس عليك كتابة Map[int, int](...): يستنتج المترجم T=int, U=int.

في بعض الأحيان لا يكون الاستدلال كافيًا (على سبيل المثال، المُنشئات التي لا تحتوي على وسيطات T) ويلزم إنشاء مثيل صريح: New[*Server]().

القيود المضمنة

القيدمعنى
محمي_INLINE_13أي نوع (== interface{}).
محمي_INLINE_15الأنواع ذات == و!=: رقمية، وسلسلة، ومنطقية، ومؤشرات، وقنوات، ومصفوفات/بنيات قابلة للمقارنة.

comparable يسمح بالمساواة ولكن لا < أو >.

Go
func Index[T comparable](s []T, x T) int {
    for i, v := range s {
        if v == x { return i }
    }
    return -1
}

بالنسبة إلى </>، تحتاج إلى قيد مخصص.

القيد المخصص: اكتب النقابات

القيد هو واجهة مع عبارات "نوع العنصر":

Go
type Ordered interface {
    ~int | ~int64 | ~float64 | ~string
}

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = union: يقبل أيًا من الأنواع المدرجة.
  • ~int = "int أو أي نوع محدد كـ type X int" (بنفس النوع الأساسي). بدون ~، فقط int بالضبط.

يمكن للقيد أيضًا خلط الأساليب وكتابة العناصر:

Go
type Stringable interface {
    ~string
    Len() int
}

أنواع عامة

ليس فقط الوظائف: يمكن أن تحتوي البنيات والواجهات أيضًا على معلمات النوع.

Go
type Set[T comparable] struct {
    m map[T]struct{}
}

func NewSet[T comparable]() *Set[T] {
    return &Set[T]{m: make(map[T]struct{})}
}

func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }

s := NewSet[string]()
s.Add("ada")

لا يمكن للطرق إضافة معلمات نوع تتجاوز تلك الخاصة بالنوع: func (s *Set[T]) MapTo[U any](...) لا يتم تجميعها (قيود التصميم المتعمد).

متى تستخدم الأدوية الجنيسة

** نعم ** عندما:

  • المنطق مطابق حقًا لعدة أنواع (المجموعات والخوارزميات والمساعدين مثل Map/Filter/Reduce).
  • سيكون البديل هو تكرار الكود أو استخدام interface{} + تأكيدات النوع (فقدان أمان النوع).

لا متى:

  • الواجهة التي تحتوي على طريقة أو طريقتين كافية وأكثر قابلية للقراءة (io.Reader، fmt.Stringer).
  • أنت تقوم بتحديد المعلمات "لأنك تستطيع": حالة استخدام واحدة → إنشاء نسخة ملموسة منها.

تمارين

تمرين#go.m10.l3.e1
المحاولات: 0جارٍ التحميل…

تعريف الوظيفة العامة Min[T Ordered](a, b T) T التي تقوم بإعادة تشغيلها باستخدام المشغل <. لقد تم طلب القيد.

جارٍ تحميل المحرر…

الحل متاح بعد 3 من المحاولات

تمرين#go.m10.l3.e2
المحاولات: 0جارٍ التحميل…

تنفيذ الخريطة[T, U Any](s []T, f func(T) U) []U: تطبيق على كل عنصر واستعادة الشريحة الجديدة.

جارٍ تحميل المحرر…

الحل متاح بعد 3 من المحاولات

Quiz#go.m10.l3.e3
جاهز

القيد النوعي المدمج يوافق على المشغل == أماه NON l'operatore <?

Go
func Index[T ???](s []T, x T) int { ... }
خيارات الإجابة