泛型的约束写得对不对,编译器会立刻告诉你。这篇逐个用真实的编译器报错确认规则:comparable、联合类型与 ~、运算符、类型推断的成功与失败,以及这个版本里新出现的“泛型方法”。
Index 用于 int / string / 结构体 / 数组 / 指针: 编译通过,运行输出:2 1 1 0 0
Index 用于 []int 的切片(切片不可比较):
main.go:12:32: []int does not satisfy comparable
Index 用于 map[string]int:
main.go:12:32: map[string]int does not satisfy comparable
Index 用于 func():
main.go:12:32: func() does not satisfy comparable
含切片字段的结构体:
main.go:13:32: Bad does not satisfy comparable
1 (any 作为实参:编译通过,比较 1 与 "x" 没问题)
接口类型实参 any:Index([]any{[]int{1}}, []int{1}) 运行时 panic: runtime error: comparing uncomparable type []intcomparable 要求类型的值可以用 == 比较。切片、map、函数和含这些的结构体不满足,编译器直接拒绝。接口类型(比如 any)作为实参是允许的——这样 Index[any] 能编译,但如果装进去的动态类型不可比较,比较时会在运行时 panic(上面的 comparing uncomparable type []int)。
SumA[MyInt](约束没有 ~):
main.go:12:31: MyInt does not satisfy NumNoTilde (possibly missing ~ for int in NumNoTilde)
SumB[MyInt](约束有 ~int): 编译通过,运行输出:3 1.5int | float64 只接受恰好是这些类型的实参;~int 表示“底层类型是 int 的所有类型”,所以自定义的 MyInt 要靠 ~ 才能满足。编译器的提示已经把原因写出来了:possibly missing ~ for int。
T any 上用 >:
main.go:8:35: invalid operation: a > b (type parameter T cannot use operator >)
T comparable 上用 <:
main.go:3:47: invalid operation: a < b (type parameter T cannot use operator <)
cmp.Ordered 上用 <: 编译通过,运行输出:2 b 1.5
cmp.Ordered 的定义(标准库源码):
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}泛型函数体里只能使用约束保证存在的操作。any 什么操作都不保证,comparable 只保证 == 与 !=,要 < 就用 cmp.Ordered(标准库里它就是一个联合类型的约束)。
Max(1, 2.5)(无类型常量混用): 编译通过,运行输出: float64 2.5
Max(x, 2.5),x 是 int 变量:
main.go:13:42: cannot use 2.5 (untyped float constant) as int value in argument to Max (truncated)
Zero()(只能从返回值推断):
main.go:13:31: in call to Zero, cannot infer T (declared at ./main.go:9:11)
Zero[int]() 显式实例化: 编译通过,运行输出: 0 ""
Map(xs, strconv.Itoa) 同时推断 T 与 U: 编译通过,运行输出: []string [1 2]
First(IDs{3, 4}) 用 ~[]E 约束保留具名切片类型: 编译通过,运行输出: 3
f := Max(不实例化就当值用):
main.go:13:20: cannot use generic function Max without instantiation能推断:从函数实参(包括无类型常量混用,Max(1, 2.5) 是 float64)、从函数类型实参(Map(xs, strconv.Itoa) 推出 T 与 U)、从带 ~[]E 的约束里提取元素类型。不能推断:只在返回值里出现的类型参数,以及不实例化就把泛型函数当值使用。
go.mod 写 go 1.26:编译失败
main.go:7:24: generic method requires go1.27 or later (-lang was set to go1.26; check go.mod)
go.mod 写 go 1.27:编译通过,运行输出:[10 20 30] | func(func(int) float64) main.List[float64]
Box[int] 有泛型方法 Map,能实现 interface{ Map(func(int) string) string } 吗:
main.go:10:17: cannot use Box[int]{…} (value of struct type Box[int]) as Mapper value in variable declaration: Box[int] does not implement Mapper (wrong type for method Map)
接口方法声明类型参数:
main.go:3:27: interface method must have no type parameters
main.go:3:45: undefined: U
不实例化就把泛型方法当值:
main.go:9:7: cannot use generic function b.Map without instantiation读规范(doc/go_spec.html 里的 “generic method [Go 1.27]”)可以看到:方法声明自己的类型参数,实例化后才能调用或当值使用。实验证实它受语言版本控制(go.mod 写 1.26 就报错),并且有两条限制:带类型参数的方法与接口里的普通方法签名不相同,所以不能用来实现那个接口;接口自己的方法不能再声明类型参数。我这里只验证了编译与简单运行,没有展开其它规则。