Chuyển đến nội dung chính
Mô-đun 10 · Bài học 3 trong tổng số 548/50 trong khóa học~15 min
Bài học theo mô-đun (3/5)

Thuốc gốc (Đi 1.18+)

Kể từ phiên bản Go 1.18 (tháng 3 năm 2022), thông số loại đã có sẵn: các hàm và loại có thể được tham số hóa theo loại. Chúng cho phép bạn viết Map, Filter, Set[T], LinkedList[T] mà không cần sao chép mã và không cần interface{} + xác nhận loại thời gian chạy.

Hàm chung: cú pháp

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}

Ba yếu tố mới:

  • [T, U any] sau tên: khai báo các tham số kiểu cùng với các ràng buộc của chúng.
  • any = ràng buộc chấp nhận bất kỳ loại nào (bí danh interface{} kể từ Phiên bản 1.18).
  • Suy luận: gọi Map([]int{...}, ...) bạn không cần phải viết Map[int, int](...): trình biên dịch suy ra T=int, U=int.

Đôi khi suy luận là không đủ (ví dụ: các hàm tạo không có đối số T) và cần có sự khởi tạo rõ ràng: New[*Server]().

Các ràng buộc tích hợp

Ràng buộcÝ nghĩa
anyBất kỳ loại nào (== interface{}).
comparableCác loại có == và !=: số, chuỗi, bool, con trỏ, kênh, mảng/cấu trúc có thể so sánh.

comparable cho phép sự bình đẳng nhưng không < hoặc >.

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

Đối với </> bạn cần ràng buộc tùy chỉnh.

Ràng buộc tùy chỉnh: loại công đoàn

Ràng buộc là một giao diện với mệnh đề "loại phần tử":

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

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = union: chấp nhận bất kỳ loại nào được liệt kê.
  • ~int = "int hoặc bất kỳ loại nào được xác định là type X int" (có cùng loại cơ bản). Không có ~, chỉ chính xác int.

Một ràng buộc cũng có thể kết hợp các phương thức và kiểu phần tử:

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

Các loại chung

Không chỉ các hàm: cấu trúc và giao diện cũng có thể có các tham số kiểu.

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")

Các phương thức KHÔNG THỂ thêm các tham số loại ngoài các tham số thuộc loại: func (s *Set[T]) MapTo[U any](...) không biên dịch (giới hạn thiết kế có chủ ý).

Khi nào nên sử dụng thuốc generic

Có khi:

  • Logic thực sự giống hệt đối với một số loại (bộ sưu tập, thuật toán, trình trợ giúp như Map/Filter/Reduce).
  • Giải pháp thay thế sẽ là sao chép mã hoặc sử dụng interface{} + xác nhận loại (mất tính an toàn về loại).

Không khi:

  • Giao diện có một hoặc hai phương thức là đủ và dễ đọc hơn (io.Reader, fmt.Stringer).
  • Bạn đang tham số hóa "bởi vì bạn có thể": một trường hợp sử dụng duy nhất → tạo một phiên bản cụ thể của nó.

Bài tập

tập thể dục#go.m10.l3.e1
Nỗ lực: 0Đang tải…

Định nghĩa các funzione generica Min[T Ordered](a, b T) T che ritorna il Minore usando l'operatore <. Il hạn chế Đặt hàng è già fornito.

Đang tải trình chỉnh sửa…

Giải pháp khả dụng sau 3 lần thử

tập thể dục#go.m10.l3.e2
Nỗ lực: 0Đang tải…

Bản đồ triển khai[T, U bất kỳ](s []T, f func(T) U) []U: ứng dụng cho một phần tử và phần còn lại của phần mới.

Đang tải trình chỉnh sửa…

Giải pháp khả dụng sau 3 lần thử

Quiz#go.m10.l3.e3
Sẵn sàng

Ràng buộc chất lượng được tích hợp sẵn trong sự đồng ý của người vận hành == ma NON l'operatore <?

Go
func Index[T ???](s []T, x T) int { ... }
Tùy chọn trả lời