Go Generics ใช้งานยังไงในเวอร์ชันใหม่

Sharing is caring!

อัปเดตล่าสุด: รองรับ Go เวอร์ชัน 1.22

Generics เป็นหนึ่งในฟีเจอร์ที่ถูกพูดถึงมานานใน Go และในที่สุดก็เปิดตัวอย่างเป็นทางการใน Go 1.18 และพัฒนาขึ้นต่อเนื่องถึง Go 1.22 ในบทความนี้เราจะมาเจาะลึกการใช้งาน Go Generics ทั้งพื้นฐานและเทคนิคขั้นสูง พร้อมตัวอย่างจริงที่สามารถนำไปใช้ได้ทันที


Generics คืออะไร?

Generics ช่วยให้เราสามารถเขียนโค้ดที่ใช้ซ้ำได้โดยไม่ต้องเจาะจงชนิดของข้อมูลล่วงหน้า โดยใช้ “type parameters” ทำให้เราสามารถทำงานกับหลายชนิดข้อมูลได้จากโค้ดเดียวกัน

ตัวอย่างที่ไม่ใช้ Generics

func DoubleInts(slice []int) []int {
    result := make([]int, len(slice))
    for i, v := range slice {
        result[i] = v * 2
    }
    return result
}

ใช้ Generics แทนได้แบบนี้

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

จากตัวอย่างด้านบน ฟังก์ชัน Map สามารถใช้กับ slice ของอะไรก็ได้ ไม่ใช่แค่ []int


Syntax พื้นฐานของ Generics

การเขียน Generics จะใช้เครื่องหมาย [T any] เพื่อประกาศพารามิเตอร์ชนิดข้อมูล (type parameter)

  • T คือชื่อของพารามิเตอร์ชนิดข้อมูล
  • any หมายถึง T สามารถเป็นอะไรก็ได้

การใช้งานกับ Struct

type Box[T any] struct {
    Value T
}

func (b Box[T]) Get() T {
    return b.Value
}

เรียกใช้งาน

intBox := Box[int]{Value: 10}
fmt.Println(intBox.Get()) // 10

strBox := Box[string]{Value: "hello"}
fmt.Println(strBox.Get()) // hello

การกำหนด Constraint

Generics ไม่ได้จำกัดแค่ type ที่เป็น any เราสามารถกำหนด constraint ได้ เช่น ตัวเลข หรือ type ที่มี method ใด ๆ

type Number interface {
    ~int | ~float64
}

func Sum[T Number](a, b T) T {
    return a + b
}

ในตัวอย่างนี้ T จะต้องเป็นชนิดข้อมูลที่สามารถบวกกันได้เท่านั้น


การใช้หลาย Type Parameter

func CompareAndPrint[K comparable, V any](m map[K]V) {
    for k, v := range m {
        fmt.Printf("%v => %v\n", k, v)
    }
}

K ต้องเป็น comparable เพื่อใช้เป็น key ใน map ได้ ส่วน V สามารถเป็นอะไรก็ได้


Generics กับ Method Receivers

type Pair[T any] struct {
    First, Second T
}

func (p Pair[T]) Swap() Pair[T] {
    return Pair[T]{First: p.Second, Second: p.First}
}

Method Receiver ของ Struct ที่ใช้ Generics ก็สามารถใช้พารามิเตอร์ type ได้เช่นกัน


Best Practices ในการใช้ Go Generics

  • ใช้ชื่อ type parameter สั้น เช่น T, K, V เว้นแต่จำเป็นต้องชัดเจน
  • หลีกเลี่ยงการทำ constraint ซับซ้อนเกินไป
  • ใช้ Generics เฉพาะกรณีที่ได้ประโยชน์จริง

อัปเดตล่าสุดใน Go 1.21 – 1.22

  • ปรับปรุงการ infer type ให้ดีขึ้น
  • รองรับ type sets ที่ซับซ้อนยิ่งขึ้น
  • ใช้ constraint จาก interface เดิมที่ประกาศไว้ได้

ดู release note เพิ่มเติม: go.dev/doc/go1.22


สรุป

Go Generics เป็นฟีเจอร์ที่ทรงพลัง ช่วยให้เราสามารถเขียนโค้ดที่ยืดหยุ่น ปลอดภัย และ maintain ได้ง่ายขึ้นในระยะยาว โดยเฉพาะกับการเขียน reusable libraries และ data structure หากคุณยังไม่เคยลอง แนะนำให้เริ่มจากฟังก์ชันทั่วไปและค่อย ๆ ขยายไปใช้ใน Struct และ Method


แหล่งอ้างอิง


คำค้น (SEO Keywords)

  • Go Generics
  • Generics Go 1.22
  • การใช้ Generics ในภาษา Go
  • Go Generics Constraints
  • Go Generics กับ Struct
  • Generics Method Receiver
  • type parameters Go

Leave a Reply

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *