Portal/Notes πŸ“
Golang

Generics

Memahami generics di Go β€” type parameter, constraint, dan contoh penggunaan.

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

Generics di Go (diperkenalkan di Go 1.18) memungkinkan Anda mendefinisikan fungsi atau tipe yang dapat bekerja dengan tipe data apa pun sambil mempertahankan type-checking yang ketat. Ini menghilangkan kebutuhan akan kode boilerplate atau menggunakan tipe interface{} yang kurang aman pada saat kompilasi.

Konsep Utama

Type Parameters

func PrintSlice[T any](slice []T) {
    for _, v := range slice {
        fmt.Println(v)
    }
}
  • Generics menggunakan type parameter dalam kurung siku [] untuk menentukan tipe yang dapat diterima oleh suatu fungsi atau tipe.
  • Di sini, T adalah type parameter yang dapat mewakili tipe apa pun.

Constraints

func Add[T int | float64](a, b T) T {
    return a + b
}
  • Constraint mendefinisikan operasi apa yang diizinkan pada type parameter.
  • Di sini, T dibatasi hanya ke int atau float64, memastikan operator + valid.

Constraint any

  • Keyword any adalah alias untuk interface{} dan mewakili tipe apa pun tanpa batasan.

Manfaat

  • Compile-Time Safety: Compiler Go menegakkan kebenaran tipe pada saat kompilasi. Mengurangi error runtime.
  • Monomorphization: Untuk performa, compiler menghasilkan versi spesifik dari fungsi/tipe generik untuk setiap tipe yang digunakan. Ini menghindari overhead runtime.
  • Code Reusability: Menghindari duplikasi fungsi atau struktur data yang serupa.
  • Performa: Tidak seperti interface{}, generics tidak memerlukan type assertion atau reflection.

Contoh

Generic Functions

Fungsi yang bekerja dengan tipe apa pun

package main

import "fmt"

func PrintSlice[T any](slice []T) {
    for _, v := range slice {
        fmt.Println(v)
    }
}

func main() {
    PrintSlice([]int{1, 2, 3})
    PrintSlice([]string{"a", "b", "c"})
}

Output:

1
2
3
a
b
c

Generic Types

Mendefinisikan struktur data dengan type parameter:

package main

import "fmt"

// Stack adalah generic stack
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() T {
    last := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return last
}

func main() {
    s1 := Stack[int]{}
    s1.Push(10)
    s1.Push(20)
    fmt.Println(s1.Pop()) // 20
    fmt.Println(s1.Pop()) // 10

    s2 := Stack[string]{}
    s2.Push("A")
    s2.Push("B")
    fmt.Println(s2.Pop()) // B
    fmt.Println(s2.Pop()) // A
}

Output:

20
10
B
A
  • Pada contoh di atas kita dapat membuat stack yang bekerja dengan tipe apa pun.
  • Ini lebih aman daripada menggunakan interface{} karena tipenya dipaksakan pada saat kompilasi, bukan di-casting manual saat runtime.

Multiple Type Parameters

Fungsi atau tipe dengan lebih dari satu type parameter:

package main

import "fmt"

func Combine[K comparable, V any](keys []K, values []V) map[K]V {
    result := make(map[K]V)
    for i, key := range keys {
        result[key] = values[i]
    }
    return result
}

func main() {
    keys := []string{"a", "b", "c"}
    values := []int{1, 2, 3}
    fmt.Println(Combine(keys, values))

    // tukar posisinya
    fmt.Println(Combine(values, keys))
}

Output:

map[a:1 b:2 c:3]
map[1:a 2:b 3:c]
  • Pada contoh di atas kita membuat map generik yang dapat menerima tipe any sebagai value-nya.
  • Perhatikan bahwa kita menggunakan comparable alih-alih any karena map di Go membutuhkan ini untuk menghindari ambiguitas, inefisiensi, dan potensi bug terkait tipe yang mutable atau tidak comparable.

New Constraints

Membatasi type parameter dengan constraint:

package main

import "fmt"

type Number interface {
    int | int64 | float64
}

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

func main() {
    fmt.Println(Sum(1, 1))
    fmt.Println(Sum(1.1, 1.1))
}

Output:

2
2.2
  • Pada contoh di atas kita membatasi fungsi Sum untuk hanya menerima tipe Number yang kita definisikan sebagai int, int64, atau float64.

  • Jika kita mencoba memasukkan string sebagai parameter Sum("A", "B") maka akan mengembalikan error:

    string does not satisfy Number (string missing in int | int64 | float64)

Edit on GitHub

Last updated on

Generics | Faisal Affan