go泛型函数必须写成func foo[t constraint](x t)形式,约束不可省略;comparable适用于比较和map键,数值运算需自定义含~的接口;类型推断易失效,结构体需显式指定类型实参;不依赖t内部操作时应优先用interface{}。

func 后面直接跟 [T constraint] 才是 Go 泛型函数的正确定义方式,所谓“函数模板”只是对泛型的误称——Go 没有 C++ 那种运行前展开的模板机制,而是编译期实例化 + 类型擦除优化的组合。
泛型函数怎么写:语法和约束必须同时存在
写一个泛型函数不是加个 [T any] 就完事。如果只写 func Foo[T any](x T),你连 x == x 都做不了,因为 any 不保证可比较;想做加法?更不行——编译器根本不知道 T 支持 +。
-
comparable是最常用约束,适用于需要==、!=或用作 map key 的场景(比如查找、去重、缓存键) - 数值运算必须自定义约束,例如:
type Number interface{ ~int | ~float64 },注意~表示底层类型匹配,type MyInt int也能满足 - 函数名和
[T ...]之间不能有空格,func Foo [T any]是语法错误,会报expected ']', found 'T' - 多个类型参数用逗号分隔:
[K comparable, V any],不是分号,也不是[K comparable][V any]
类型推断失效的常见情况
Go 编译器能自动推导 T 的场景有限。一旦参数不足以唯一确定类型,就必须显式写出类型实参。
- 传
nil:比如Foo(nil),编译器无法知道T是*string还是*int - 参数类型不一致:如
Sum(1, 2.0),int和float64无法统一为同一个T - 接收接口值:若函数签名是
func Print[T fmt.Stringer](v T),但你传的是interface{}变量,推断失败 - 调用链中间有类型擦除:比如从
map[string]interface{}取值再传入泛型函数,interface{}本身不是具体类型,推断中断
泛型结构体初始化时容易漏掉类型实参
泛型结构体不是“模板类”,Stack[T] 本身不是类型,Stack[int] 和 Stack[string] 才是两个完全独立的类型。初始化时漏写类型,编译直接报错。
- 不能写
Stack{}—— 编译器提示undefined: Stack - 正确写法是:
Stack[int]{}或var s Stack[string] -
new(Stack[int])合法,返回*Stack[int],但字段仍是未初始化零值 - 字段类型必须用
T,比如data []T;写成[]interface{}就失去泛型意义,退化为运行时类型检查
什么时候不该用泛型,而该用 interface{}
泛型不是万能胶。只要不依赖类型内部行为(比如不比较、不运算、不调方法),硬套泛型反而增加认知负担和编译体积。
- 日志函数:
Log(v interface{})比Log[T any](v T)更轻量,且无额外收益 - 序列化入口:
json.Marshal(v interface{})内部本就要反射,泛型无法提速,还限制了输入范围 - 简单容器透传:比如
type Wrapper[T any] struct{ v T },若只做包装/解包,没类型相关逻辑,不如直接struct{ v interface{} } - 混合类型集合:如
[]interface{}存不同结构体,泛型无法表达这种异构性
真正关键的分界点在于:是否在函数体内对 T 做了编译期可验证的操作。否则,泛型只是给代码加了一层不必要的壳。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











