Generics في Go: لماذا هي ضرورية وكيفية استخدامها

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

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

ابدأ الدورة
```html

مقدمة: ما هي Generics ولماذا ظهرت في Go

لفترة طويلة، استغنى مجتمع Go عن الأدوية (الأنواع العامة). استخدم المطورون interface{} والتحويل اليدوي للأنواع، مما أدى إلى كود ضخم وأخطاء أثناء التنفيذ. مع إصدار Go 1.18 (فبراير 2022)، تمت إضافة الأدوية رسميًا إلى اللغة - وهي واحدة من أكثر الميزات المنتظرة.

تسمح Generics (أو تعدد الأشكال البارامتري) بكتابة دوال وهياكل بيانات تعمل مع أي نوع، مع الحفاظ على الكتابة الصارمة في مرحلة التجميع. هذا يعني أنه يمكنك كتابة خوارزمية واحدة (مثل الفرز أو البحث) واستخدامها مع int و string والهياكل المخصصة - دون تكرار الكود ودون فقدان أمان الأنواع.

في هذه المقالة، سنشرح لماذا نحتاج إلى الأدوية في Go، وكيفية استخدامها بشكل صحيح، وما هي المخاطر التي يجب مراعاتها.

الجزء الرئيسي: كيف تعمل Generics في Go

1. بناء جملة معاملات الأنواع

يبدو البناء الأساسي للأدوية في Go كالتالي: بعد اسم الدالة أو النوع، تُحدد معاملات الأنواع بين قوسين مربعين. لنلقِ نظرة على أبسط مثال - دالة تُعيد القيمة المُمررة (مشابهة لـ identity):

package main

import "fmt"

// دالة عامة: T هو معامل النوع
func Print[T any](value T) {
    fmt.Println(value)
}

func main() {
    Print[int](42)       // تحديد النوع بشكل صريح
    Print[string]("Go")  // تحديد النوع بشكل صريح
    Print("Go 1.18")     // استنتاج النوع ضمنيًا (type inference)
}

الكلمة المفتاحية any هي اسم مستعار لـ interface{}. تعني أن الدالة تقبل قيمة من أي نوع. لكن القوة الحقيقية للأدوية تتجلى عند استخدام القيود (constraints).

2. القيود (constraints) والواجهات

تسمح القيود بتحديد العمليات التي يمكن تنفيذها مع معامل النوع. على سبيل المثال، لجمع رقمين، يجب ضمان أن النوع يدعم العوامل + و - و * إلخ. في Go، تُستخدم لهذا واجهات خاصة من حزمة golang.org/x/exp/constraints أو واجهات مخصصة:

package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// القيد: يجب أن يدعم T المقارنة
func Max[T constraints.Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Max(10, 20))       // 20
    fmt.Println(Max("apple", "banana")) // banana
    // fmt.Println(Max([]int{1}, []int{2})) // خطأ في التجميع: الشرائح غير مرتبة
}

الواجهة المضمنة constraints.Ordered تشمل الأنواع التي تدعم العوامل < و <= و > و >=: جميع الأنواع الرقمية و string.

يمكنك أيضًا إنشاء قيود مخصصة:

// قيد مخصص: يجب أن يحتوي النوع على طريقة String()
type Stringer interface {
    String() string
}

func Describe[T Stringer](v T) string {
    return "Value: " + v.String()
}

3. الهياكل والطرق العامة

تعمل الأدوية في Go ليس فقط مع الدوال، ولكن أيضًا مع الأنواع المخصصة. لنلقِ نظرة على مثال كلاسيكي - مكدس عام:

package main

import "fmt"

// هيكل عام Stack
type Stack[T any] struct {
    items []T
}

// Push يضيف عنصرًا إلى المكدس
func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

// Pop يستخرج عنصرًا من المكدس
func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    last := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return last, true
}

func main() {
```                

المدونات

توصيات الكتب