Go 1.23 泛型进阶:用类型约束写出更优雅的通用代码

Go 1.23 Advanced Generics: Writing Elegant Generic Code with Type Constraints

| David Wang | 2026-07-28T08:24:58

Go 泛型出来好几个版本了,但很多人还停留在最基础的用法。这篇文章介绍几个高级技巧,让你的泛型代码更实用。

Advanced Go generics techniques including type constraints, constraint composition, and practical patterns for writing reusable generic code.

## 不要只会 `[T any]` Go 泛型已经出来好几个版本了,但我在 Code Review 里经常看到大家还是只会写 `[T any]`,然后在函数里用类型断言。这完全没有发挥泛型的威力。 ## 技巧一:用联合类型约束 ```go // 限定 T 只能是数值类型 type Number interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64 } func Sum[T Number](nums []T) T { var total T for _, n := range nums { total += n } return total } // 使用 fmt.Println(Sum([]int{1, 2, 3})) // 6 fmt.Println(Sum([]float64{1.1, 2.2})) // 3.3 ``` `~int` 前面的波浪号表示"底层类型是 int 的所有类型"。比如你定义了 `type UserID int`,`UserID` 也能匹配 `~int`。 ## 技巧二:约束组合 ```go // 定义多个约束 type Stringer interface { String() string } type Comparable interface { comparable } // 组合约束:既要可比较,又要能转字符串 type ComparableStringer interface { comparable String() string } func FindAndPrint[T ComparableStringer](items []T, target T) { for _, item := range items { if item == target { fmt.Println("Found:", item.String()) return } } fmt.Println("Not found") } ``` ## 技巧三:泛型 Repository 模式 这是我们项目里实际在用的模式: ```go type Entity interface { GetID() string TableName() string } type Repository[T Entity] struct { db *sql.DB } func NewRepository[T Entity](db *sql.DB) *Repository[T] { return &Repository[T]{db: db} } func (r *Repository[T]) FindByID(id string) (T, error) { var entity T table := entity.TableName() query := fmt.Sprintf("SELECT * FROM %s WHERE id = ?", table) // ... 查询逻辑 return entity, nil } func (r *Repository[T]) Save(entity T) error { table := entity.TableName() // ... 保存逻辑 return nil } // 使用 type User struct { /* ... */ } func (u User) GetID() string { return u.ID } func (u User) TableName() string { return "users" } userRepo := NewRepository[User](db) user, _ := userRepo.FindByID("123") ``` 一套代码,所有实体通用。 ## 技巧四:泛型 Result 类型 受 Rust 启发,用泛型实现 Result 类型: ```go type Result[T any] struct { value T err error } func Ok[T any](value T) Result[T] { return Result[T]{value: value} } func Err[T any](err error) Result[T] { return Result[T]{err: err} } func (r Result[T]) Unwrap() (T, error) { return r.value, r.err } func Map[T, U any](r Result[T], fn func(T) U) Result[U] { if r.err != nil { return Err[U](r.err) } return Ok(fn(r.value)) } ``` ## 技巧五:泛型函数选项模式 ```go type Option[T any] func(*T) func WithField[T any, V any](setter func(*T, V), value V) Option[T] { return func(t *T) { setter(t, value) } } func New[T any](opts ...Option[T]) T { var t T for _, opt := range opts { opt(&t) } return t } ``` ## 总结 Go 的泛型虽然不像 Rust 那样强大,但已经足够解决大部分代码复用的问题了。关键是要善用类型约束,而不是到处 `interface{}` 然后做类型断言。 我的经验是:**先写具体实现,发现重复了再抽泛型**。不要为了泛型而泛型,Go 的哲学还是简单直接。


## Beyond Basic Generics Many Go developers still only use `[T any]` with type assertions. This article covers advanced generics techniques for writing more practical and type-safe generic code. ## Key Techniques 1. **Union type constraints** with `~` for underlying type matching 2. **Constraint composition** combining comparable and interface requirements 3. **Generic Repository pattern** for database access layer 4. **Result type** inspired by Rust for functional error handling 5. **Generic functional options** pattern ## Philosophy Start with concrete implementations, extract generics when you see repetition. Don't use generics for the sake of generics - Go's philosophy is still simplicity and directness.

← Back to News