Generics were introduced in Go 1.18 (2022). They allow you to write code that works with multiple types without losing type-safety. Before generics, Go programmers had to use empty interface (interface) or duplicate code for each type. Generics solve this problem.
Generics đã được giới thiệu trong Go 1.18 (2022). Chúng cho phép bạn viết code mà hoạt động với nhiều kiểu dữ liệu mà không mất type-safety. Trước khi có generics, Go lập trình viên phải sử dụng empty interface (interface{}) hoặc viết lại code cho mỗi kiểu. Generics giải quyết vấn đề này.
Type parameters are defined in square brackets []. For example, func Map[T, U any](s []T, f func(T) U) []U is a generic function with two type parameters T and U. When you call Map, Go infers the types or you can specify them explicitly.
Type parameter được định nghĩa trong square bracket []. Ví dụ, func Map[T, U any](s []T, f func(T) U) []U là một hàm generic có hai type parameter T và U. Khi bạn gọi Map, Go sẽ suy ra (infer) kiểu dữ liệu hoặc bạn có thể chỉ định rõ ràng.
1package main23import "fmt"45// Generic function with one type parameter6func Print[T any](value T) {7 fmt.Println(value)8}910// Generic function with two type parameters11func Map[T, U any](s []T, f func(T) U) []U {12 result := make([]U, len(s))A constraint limits which types can be used for a type parameter. any (or interface) means no constraint. comparable allows comparisons with ==. You can write custom constraints using interface syntax.
Constraint hạn chế những kiểu dữ liệu có thể được sử dụng cho một type parameter. any (hoặc interface{}) có nghĩa là không có constraint. comparable cho phép so sánh với ==. Bạn có thể viết custom constraint bằng interface syntax.
1package main23import "fmt"45// No constraint: any type works6func PrintAny[T any](value T) {7 fmt.Println(value)8}910// Constraint: only comparable types (support ==)11func Contains[T comparable](slice []T, value T) bool {12 for _, v := range slice {You can declare generic structs, interfaces, and types. For example, type Stack[T any] struct { items []T } is a generic stack. To use Stack, you create instances like Stack[int] or Stack[string]. Methods on generic types can also be generic.
Bạn có thể khai báo generic struct, interface, và type. Ví dụ, type Stack[T any] struct { items []T } là một generic stack. Để sử dụng Stack, bạn tạo instance như Stack[int] hoặc Stack[string]. Method trên generic type cũng có thể là generic.
1package main23import "fmt"45// Generic type: Stack6type Stack[T any] struct {7 items []T8}910// Method on generic type11func (s *Stack[T]) Push(value T) {12 s.items = append(s.items, value)A union constraint lists allowed types separated by |. For example, type Number interface { int | int32 | int64 | float32 | float64 } allows only numeric types. The tilde ~ allows underlying types: ~int matches int and any type based on int.
Union constraint liệt kê các kiểu được phép, cách nhau bằng |. Ví dụ, type Number interface { int | int32 | int64 | float32 | float64 } chỉ cho phép các kiểu số. Tilde ~ cho phép underlying type: ~int khớp với int và bất kỳ kiểu dựa trên int.
1package main23import "fmt"45// Union constraint: only specific types6type Integer interface {7 int | int32 | int64 | uint | uint32 | uint648}910// Union constraint with underlying types11type SignedInt interface {12 ~int | ~int32 | ~int64Use generics when you write the same logic for different types. Examples: generic slice functions (Filter, Map, Find), generic data structures (Stack, Queue, Tree), generic algorithms (Sort, BinarySearch). Don't over-generalize -> if logic only works with one type, generics are overkill.
Sử dụng generics khi bạn viết cùng một logic cho nhiều kiểu khác nhau. Ví dụ: generic slice functions (Filter, Map, Find), generic data structures (Stack, Queue, Tree), generic algorithms (Sort, BinarySearch). Không over-generalize → nếu logic chỉ cần với một kiểu, generics là overkill.
1package main23import (4 "fmt"5 "golang.org/x/exp/slices" // Experimental generic slice functions6)78// Example: Filter is generic, reusable9func Filter[T any](slice []T, predicate func(T) bool) []T {10 var result []T11 for _, v := range slice {12 if predicate(v) {Generics thêm độ phức tạp. Sử dụng chúng khi thực sự cần thiết. Nếu code của bạn chỉ hoạt động với một kiểu hoặc logic khác nhau đáng kể giữa các kiểu, hãy tránh generics.
Key Takeaways
Điểm Chính
- Generics use square brackets for type parameters: func Foo[T any]...Generics sử dụng square bracket cho type parameter: func Foo[T any]...
- Constraints limit which types can be used (any, comparable, custom)Constraint hạn chế những kiểu có thể được sử dụng (any, comparable, custom)
- Generic types: type Stack[T any] struct { ... }Generic type: type Stack[T any] struct { ... }
- Use generics to avoid code duplication for multiple typesSử dụng generics để tránh trùng lặp code cho nhiều kiểu
Practice
Test your understanding of this chapter
What is the difference between 'any' and 'comparable' constraints?
Sự khác biệt giữa constraint 'any' và 'comparable' là gì?
What does the tilde ~ mean in a union constraint?
Dấu ~ có nghĩa gì trong union constraint?
Generics in Go were available since Go 1.0.
Generics trong Go đã có sẵn từ Go 1.0.
A generic type like Stack[T] creates a new type for each T, so Stack[int] and Stack[string] are completely separate types.
Một generic type như Stack[T] tạo một kiểu mới cho mỗi T, vì vậy Stack[int] và Stack[string] là các kiểu hoàn toàn riêng biệt.
Create a generic function that returns the minimum of two comparable values
Tạo một hàm generic trả về giá trị nhỏ nhất của hai giá trị so sánh được
func Min[T ()] (a, b T) T { if a < b { return a } return b }