# Go 泛型（Generics）

Go 1.18 引入了泛型，这是 Go 语言自诞生以来最大的语言变更。泛型允许我们编写能够操作多种类型的通用代码，减少重复代码的同时保持类型安全。在此之前，Go 开发者只能通过 `interface{}` 配合类型断言来实现类似功能，但这种方式牺牲了编译期类型检查。

## 类型参数语法

泛型的核心思想是引入**类型参数**。函数和结构体可以接受类型参数，就像接受普通参数一样。

```go
package main

import "fmt"

// Min 返回两个有序类型中的较小值
func Min[T int | float64 | string](a, b T) T {
	if a < b {
		return a
	}
	return b
}

func main() {
	fmt.Println(Min(1, 2))       // int: 1
	fmt.Println(Min(1.5, 2.3))   // float64: 1.5
	fmt.Println(Min("a", "b"))   // string: "a"

	// 类型推断：编译器自动推断 T 的类型
	fmt.Println(Min[int](10, 20)) // 显式指定类型也可以
}
```

类型参数使用方括号 `[]` 声明，放在函数名之后、参数列表之前。调用时编译器通常能自动推断类型参数，无需显式传递。

## 类型约束（Constraints）

类型约束是对类型参数的限制，定义了该类型参数可以接受的类型集合。约束使用 `interface` 语法定义。

```go
package main

import "fmt"

// Number 约束：只允许 int、float64、float32
type Number interface {
	int | float64 | float32
}

func Sum[T Number](nums []T) T {
	var total T
	for _, n := range nums {
		total += n
	}
	return total
}

func main() {
	ints := []int{1, 2, 3, 4, 5}
	floats := []float64{1.1, 2.2, 3.3}

	fmt.Println(Sum(ints))   // 15
	fmt.Println(Sum(floats)) // 6.6
}
```

约束本质上是一个接口类型，它定义了类型参数必须满足的条件。使用 `|` 运算符可以指定多个允许的类型。

## 内置约束：comparable 与 any

Go 提供了两个内置约束：

- **`any`**：等价于 `interface{}`，允许任何类型
- **`comparable`**：允许可以使用 `==` 和 `!=` 进行比较的类型

```go
package main

import "fmt"

// Contains 检查切片中是否包含指定元素
// comparable 约束确保 T 类型可以使用 == 比较
func Contains[T comparable](slice []T, target T) bool {
	for _, v := range slice {
		if v == target {
			return true
		}
	}
	return false
}

// PrintAny 接受任意类型
func PrintAny[T any](value T) {
	fmt.Println(value)
}

func main() {
	fmt.Println(Contains([]int{1, 2, 3}, 2))     // true
	fmt.Println(Contains([]string{"a", "b"}, "c")) // false

	PrintAny(42)
	PrintAny("hello")
	PrintAny(true)
}
```

`comparable` 是一个非常有用的约束，因为很多通用算法（查找、去重、作为 map 的 key）都需要类型支持相等比较。

## 自定义约束

你可以用 `interface` 定义更复杂的约束，包括方法集和类型集合的组合。

```go
package main

import "fmt"

// Stringer 约束：要求类型实现了 String() string 方法
type Stringer interface {
	String() string
}

// 组合约束：允许 int 或 ~int（int 的底层类型）
type Integer interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

func ToString[T Stringer](v T) string {
	return v.String()
}

func Double[T Integer](n T) T {
	return n * 2
}

// MyInt 是 int 的自定义类型
type MyInt int

func main() {
	fmt.Println(Double(5))      // 10
	fmt.Println(Double(MyInt(3))) // 6（~int 允许底层类型为 int 的类型）
}
```

`~` 符号表示"底层类型"约束。`~int` 不仅匹配 `int`，还匹配所有底层类型为 `int` 的自定义类型。

![泛型类型约束](https://img.zhaojq.top/20260729161726717.png "泛型类型约束")

## 泛型函数示例

下面实现几个常用的泛型工具函数。

```go
package main

import "fmt"

// Map 对切片中每个元素应用函数 f，返回新切片
func Map[T any, U any](slice []T, f func(T) U) []U {
	result := make([]U, len(slice))
	for i, v := range slice {
		result[i] = f(v)
	}
	return result
}

// Filter 返回满足条件 f 的元素组成的新切片
func Filter[T any](slice []T, f func(T) bool) []T {
	var result []T
	for _, v := range slice {
		if f(v) {
			result = append(result, v)
		}
	}
	return result
}

// Reduce 将切片归约为单个值
func Reduce[T any, U any](slice []T, initial U, f func(U, T) U) U {
	result := initial
	for _, v := range slice {
		result = f(result, v)
	}
	return result
}

func main() {
	nums := []int{1, 2, 3, 4, 5}

	// Map: 将每个元素平方
	squares := Map(nums, func(n int) int { return n * n })
	fmt.Println("Squares:", squares) // [1 4 9 16 25]

	// Filter: 筛选偶数
	evens := Filter(nums, func(n int) bool { return n%2 == 0 })
	fmt.Println("Evens:", evens) // [2 4]

	// Reduce: 求和
	sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
	fmt.Println("Sum:", sum) // 15
}
```

## 泛型结构体

泛型不仅限于函数，结构体也可以使用类型参数。

```go
package main

import "fmt"

// Pair 存储两个可能不同类型的值
type Pair[A, B any] struct {
	First  A
	Second B
}

// Set 是一个泛型集合（基于 map 实现）
type Set[T comparable] struct {
	items map[T]struct{}
}

func NewSet[T comparable]() *Set[T] {
	return &Set[T]{items: make(map[T]struct{})}
}

func (s *Set[T]) Add(item T) {
	s.items[item] = struct{}{}
}

func (s *Set[T]) Contains(item T) bool {
	_, ok := s.items[item]
	return ok
}

func (s *Set[T]) Remove(item T) {
	delete(s.items, item)
}

func (s *Set[T]) Size() int {
	return len(s.items)
}

func main() {
	// 使用 Pair
	p := Pair[string, int]{First: "age", Second: 25}
	fmt.Printf("Pair: %s = %d\n", p.First, p.Second)

	// 使用 Set
	s := NewSet[int]()
	s.Add(1)
	s.Add(2)
	s.Add(3)
	s.Add(1) // 重复添加，无效果

	fmt.Println("Contains 2:", s.Contains(2)) // true
	fmt.Println("Size:", s.Size())             // 3

	s.Remove(2)
	fmt.Println("Size after remove:", s.Size()) // 2
}
```

## 泛型的限制与注意事项

泛型虽然强大，但也有一些限制需要注意：

**1. 不支持泛型方法**

Go 不允许在方法上使用类型参数（结构体级别的类型参数可以，但方法不能额外引入类型参数）。

```go
// 错误示例：方法不能有额外的类型参数
// func (s *Set[T]) Convert[U comparable]() *Set[U] { ... }
```

**2. 无法约束运算符**

类型约束无法限制类型支持 `<`、`+` 等运算符，只能约束方法集。因此对类型参数使用运算符时，需要确保约束合理。

**3. 编译时间增加**

泛型会增加编译时间，因为编译器需要为每种实际类型生成对应的代码（单态化）。

**4. 类型推断的局限**

某些复杂场景下，编译器无法推断类型参数，需要显式指定。

```go
package main

import "fmt"

// NewPair 辅助函数，利用类型推断简化创建
func NewPair[A, B any](a A, b B) Pair[A, B] {
	return Pair[A, B]{First: a, Second: b}
}

type Pair[A, B any] struct {
	First  A
	Second B
}

func main() {
	// 编译器自动推断 A=string, B=int
	p := NewPair("hello", 42)
	fmt.Println(p.First, p.Second)
}
```

**5. 不要过度使用泛型**

泛型适用于真正需要处理多种类型的通用数据结构或算法。如果只需要处理特定类型，使用具体类型会更简单清晰。

## 总结

Go 泛型通过类型参数和类型约束，让开发者能够编写类型安全的通用代码。核心要点包括：使用 `[T Constraint]` 语法声明类型参数；`any` 和 `comparable` 是两个内置约束；自定义约束通过 `interface` 定义类型集合；`~` 符号用于匹配底层类型；泛型支持函数和结构体，但不支持泛型方法。合理使用泛型可以显著减少重复代码，但应避免过度设计。

