# Go Generics năm 2026: Type Parameters, Constraints và Câu hỏi Phỏng vấn > Nắm vững Go generics cho phỏng vấn kỹ thuật với kiến thức về type parameters, constraints, toán tử tilde và triển khai thực tế như generic cache. - Published: 2026-07-08 - Updated: 2026-07-08 - Author: SharpSkill - Reading time: 9 min --- Go 1.18 đã giới thiệu generics vào ngôn ngữ Go vào tháng 3 năm 2022, và kể từ đó tính năng này đã phát triển qua Go 1.21, 1.22 và các phiên bản tiếp theo. Bài viết này đề cập đến các câu hỏi phỏng vấn Go generics quan trọng cho năm 2026, từ type parameters cơ bản đến các pattern constraint nâng cao mà hiring manager thường hỏi. > **Go Generics là gì?** > > Go generics cho phép viết các hàm và kiểu có thể hoạt động với bất kỳ kiểu dữ liệu nào trong khi vẫn duy trì type safety tại thời điểm biên dịch. Khác với interface sử dụng type assertion tại runtime, generics giải quyết kiểu tại thời điểm biên dịch, mang lại hiệu suất tốt hơn và phát hiện lỗi sớm hơn. ## Hiểu về Type Parameters trong Go Type parameters tạo thành nền tảng của Go generics. Type parameter là placeholder cho một kiểu được chỉ định khi hàm hoặc kiểu generic được sử dụng. ```go // generic.go // Basic generic function with type parameter T func PrintSlice[T any](items []T) { for _, item := range items { fmt.Println(item) } } // Usage - type inference determines T automatically func main() { PrintSlice([]int{1, 2, 3}) // T is int PrintSlice([]string{"a", "b"}) // T is string } ``` Cú pháp ngoặc vuông `[T any]` khai báo type parameter `T` bị ràng buộc bởi `any`. Trình biên dịch Go suy luận kiểu cụ thể từ các đối số, loại bỏ nhu cầu chỉ định kiểu rõ ràng trong hầu hết các trường hợp. ## Câu hỏi Phỏng vấn Phổ biến: Sự khác biệt giữa `any` và `comparable` là gì? Người phỏng vấn thường hỏi về các built-in constraints `any` và `comparable`. Hiểu sự khác biệt của chúng thể hiện kiến thức generics vững chắc. ```go // constraints.go // any: accepts all types (alias for interface{}) func Process[T any](value T) T { return value } // comparable: only types that support == and != func Contains[T comparable](slice []T, target T) bool { for _, v := range slice { if v == target { // This comparison requires comparable return true } } return false } // This compiles Contains([]int{1, 2, 3}, 2) // This fails: slices are not comparable // Contains([][]int{{1}, {2}}, []int{1}) ``` Constraint `comparable` giới hạn type parameters chỉ với các kiểu hỗ trợ toán tử bằng. Slice, map và function bị loại trừ vì Go không định nghĩa equality cho chúng ở cấp độ ngôn ngữ. ## Tạo Type Constraints Tùy chỉnh với Interface Go sử dụng interface để định nghĩa constraints tùy chỉnh, mở rộng những gì hàm generic có thể chấp nhận. [Đặc tả Go](https://go.dev/ref/spec#Type_constraints) định nghĩa cách các type element hoạt động trong constraint interface. ```go // number.go // Custom constraint using type union type Number interface { int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 } // Generic function constrained to numeric types func Sum[T Number](values []T) T { var total T for _, v := range values { total += v // + operator works because all Number types support it } return total } func main() { fmt.Println(Sum([]int{1, 2, 3})) // 6 fmt.Println(Sum([]float64{1.5, 2.5})) // 4.0 } ``` Type union với `|` chỉ định chính xác kiểu nào thỏa mãn constraint. Cách tiếp cận này cung cấp nhiều quyền kiểm soát hơn `any` trong khi tránh type assertion tại runtime. ## Câu hỏi Phỏng vấn: Giải thích Toán tử Tilde `~` trong Constraints Toán tử tilde `~` trong constraints khớp cả một kiểu và tất cả các kiểu có kiểu đó làm underlying type. Câu hỏi này kiểm tra hiểu biết về hệ thống kiểu của Go. ```go // underlying.go // ~int matches int and any type with int as underlying type type Signed interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 } // Custom type with int as underlying type type UserID int type Temperature int64 func Abs[T Signed](value T) T { if value < 0 { return -value } return value } func main() { var id UserID = -42 var temp Temperature = -10 fmt.Println(Abs(id)) // 42 - works because ~int matches UserID fmt.Println(Abs(temp)) // 10 - works because ~int64 matches Temperature } ``` Không có `~`, constraint `int` sẽ chỉ khớp chính xác kiểu `int`, không phải các kiểu tùy chỉnh như `UserID`. Tilde mở rộng constraint để bao gồm các kiểu dẫn xuất, làm cho code generic linh hoạt hơn. ## Kiểu Generic: Struct và Method Kiểu generic mở rộng ra ngoài hàm đến struct và method. Pattern này xuất hiện thường xuyên trong các triển khai cấu trúc dữ liệu. ```go // stack.go // Generic Stack type type Stack[T any] struct { items []T } // Push adds an element to the stack func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) } // Pop removes and returns the top element func (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T // Zero value for type T return zero, false } index := len(s.items) - 1 item := s.items[index] s.items = s.items[:index] return item, true } func main() { intStack := Stack[int]{} intStack.Push(10) intStack.Push(20) val, ok := intStack.Pop() // val=20, ok=true } ``` Lưu ý rằng các method trên kiểu generic phải lặp lại type parameter trong ngoặc vuông nhưng không thể giới thiệu type parameters mới. Receiver `(s *Stack[T])` liên kết method với instantiation cụ thể của `Stack`. ## Câu hỏi Phỏng vấn: Tại sao Method Không thể có Type Parameters Riêng? Go cấm type parameters bổ sung trên method, một quyết định thiết kế gây ngạc nhiên cho developer đến từ các ngôn ngữ như Java hoặc C#. [Đề xuất Go generics](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md) giải thích rằng giới hạn này tồn tại để giữ cho hệ thống kiểu có thể quản lý được. ```go // This is NOT valid Go code type Container[T any] struct { value T } // ERROR: methods cannot have type parameters // func (c *Container[T]) Transform[U any](fn func(T) U) U { // return fn(c.value) // } // Valid alternative: use a standalone function func Transform[T, U any](c *Container[T], fn func(T) U) U { return fn(c.value) } ``` Giải pháp thay thế sử dụng các hàm top-level với nhiều type parameters thay vì method. Thiết kế này giữ cho method dispatch đơn giản và tránh các tương tác phức tạp giữa kiểu receiver và type parameters của method. ## Type Inference và Constraint Inference Type inference của Go giảm sự dài dòng khi gọi hàm generic. Hiểu khi nào cần đối số kiểu rõ ràng giúp viết code sạch hơn. ```go // inference.go func Map[T, R any](input []T, transform func(T) R) []R { result := make([]R, len(input)) for i, v := range input { result[i] = transform(v) } return result } func main() { numbers := []int{1, 2, 3} // Type inference: T=int, R=string inferred from arguments strings := Map(numbers, func(n int) string { return fmt.Sprintf("%d", n) }) // Explicit types sometimes required for complex cases // Map[int, string](numbers, converter) } ``` Type inference hoạt động từ đối số hàm đến type parameters. Khi compiler không thể suy luận kiểu chỉ từ đối số, đối số kiểu rõ ràng trong ngoặc vuông giải quyết sự mơ hồ. ## Package Constraints: `cmp` và `slices` trong Standard Library Go 1.21 đã thêm package `cmp` với constraint `Ordered` và các hàm so sánh. Package `slices` minh họa code generic idiomatical trong standard library. ```go // stdlib.go import ( "cmp" "slices" ) func main() { numbers := []int{3, 1, 4, 1, 5, 9} // slices.Sort uses cmp.Ordered constraint internally slices.Sort(numbers) // [1, 1, 3, 4, 5, 9] // Binary search on sorted slice index, found := slices.BinarySearch(numbers, 4) // cmp.Compare returns -1, 0, or 1 result := cmp.Compare(3, 5) // -1 // cmp.Or returns first non-zero value value := cmp.Or(0, 0, 42, 100) // 42 } ``` [Tài liệu standard library](https://pkg.go.dev/cmp) cho thấy cách các package này tận dụng generics cho các thao tác type-safe trên các kiểu có thứ tự. Sự quen thuộc với các package này thể hiện kiến thức generics thực tế vượt ra ngoài hiểu biết lý thuyết. ## Câu hỏi Phỏng vấn: Triển khai Generic Cache Một bài tập phỏng vấn phổ biến yêu cầu ứng viên triển khai cache generic, thread-safe. Điều này kiểm tra kiến thức generics, concurrency với [package sync](/technologies/go/interview-questions/sync-primitives), và thiết kế API. ```go // cache.go import ( "sync" "time" ) type Cache[K comparable, V any] struct { mu sync.RWMutex items map[K]cacheItem[V] } type cacheItem[V any] struct { value V expiration time.Time } func NewCache[K comparable, V any]() *Cache[K, V] { return &Cache[K, V]{ items: make(map[K]cacheItem[V]), } } func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = cacheItem[V]{ value: value, expiration: time.Now().Add(ttl), } } func (c *Cache[K, V]) Get(key K) (V, bool) { c.mu.RLock() defer c.mu.RUnlock() item, exists := c.items[key] if !exists || time.Now().After(item.expiration) { var zero V return zero, false } return item.value, true } ``` Triển khai này sử dụng `comparable` cho key (yêu cầu của map) và `any` cho value. Struct `cacheItem` riêng biệt cho thấy cách các kiểu generic có thể lồng nhau. Thread safety đến từ `sync.RWMutex`, một pattern được đề cập trong [phỏng vấn concurrency](/technologies/go/interview-questions/concurrency-patterns) Go. ## Zero Values và Type Constraints Xử lý zero values trong code generic đòi hỏi hiểu biết về cách Go khởi tạo biến của kiểu có tham số. ```go // zero.go // Return zero value when slice is empty func First[T any](slice []T) T { if len(slice) == 0 { var zero T // Zero value: 0 for int, "" for string, nil for pointers return zero } return slice[0] } // Alternative: return pointer to avoid ambiguity func FirstOrNil[T any](slice []T) *T { if len(slice) == 0 { return nil } return &slice[0] } ``` Pattern `var zero T` tạo zero value cho bất kỳ kiểu `T` nào. Đối với các kiểu mà zero là giá trị hợp lệ (như `0` cho integer), biến thể trả về pointer phân biệt giữa "không tìm thấy" và "tìm thấy zero." ## Nâng cao: Kết hợp Nhiều Constraints Các hàm generic phức tạp có thể yêu cầu các kiểu thỏa mãn nhiều constraints. Go xử lý điều này thông qua interface embedding. ```go // combined.go // Constraint requiring both ordering and string conversion type Stringable interface { String() string } type OrderedStringable interface { cmp.Ordered Stringable } // Alternative: use type parameters with multiple constraints inline func PrintSorted[T interface{ cmp.Ordered; fmt.Stringer }](items []T) { slices.Sort(items) for _, item := range items { fmt.Println(item.String()) } } ``` Interface embedding kết hợp các constraints, yêu cầu các kiểu triển khai tất cả các interface được embed. Cú pháp inline `interface{ A; B }` cung cấp cùng chức năng mà không cần khai báo kiểu constraint có tên. ## Kết luận - Type parameters với `[T any]` cho phép viết code có thể tái sử dụng, type-safe mà không cần runtime reflection - `comparable` giới hạn với các kiểu hỗ trợ `==` và `!=`, cần thiết cho map keys - Toán tử tilde `~` khớp các kiểu có underlying type cụ thể, mở rộng tính linh hoạt của constraint - Constraints tùy chỉnh sử dụng type union (`int | string`) để chỉ định chính xác các kiểu được phép - Method không thể có type parameters riêng; sử dụng hàm top-level như một giải pháp thay thế - Các package standard library `cmp` và `slices` minh họa các pattern generic idiomatical - Zero values thông qua `var zero T` xử lý dữ liệu trống hoặc thiếu trong hàm generic - Cấu trúc dữ liệu generic thread-safe kết hợp generics với các sync primitive --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/go/go-generics-type-parameters-constraints-interview-questions