Go 泛型入门:给忙到啃键盘的土拨鼠一份速食指南

Golang

🕰 前情提要:Go 泛型,2022 年才来,但迟到总比不到强

就像你妈在你 25 岁生日那天终于送了辆自行车——
“早干嘛去了?”
“……在造更稳的轴承。”

在泛型降临前,Go 程序员靠三招续命:

  1. interface{} + 断言 → 运行时惊喜盲盒 🎁(panic: interface conversion: interface {} is string, not int
  2. 代码生成(go generate → 写一份逻辑,生成 8 份 .go,Git 提交时手抖 😅
  3. 说服老板:我们不需要复用 → 高风险高回报 🎯(慎用)

🪄 泛型是什么?——“函数的填空题”

普通函数:

func CopySliceInt(src []int) []int { /* ... */ }
func CopySliceString(src []string) []string { /* ... */ }
// func CopySliceCat(src []Cat) []Cat { /* 求你了,住手 */ }

泛型函数:

func CopySlice[T any](src []T) []T {
    dst := make([]T, len(src))
    copy(dst, src)
    return dst
}

✅ 一行搞定万物
✅ 编译时检查类型(不是运行时开盲盒)
T 是占位符,any 是“啥都行”(相当于 interface{} 的编译时版)

💬 土拨鼠小声:
“所以……T 就是函数界的‘此处填姓名’?”
✅ 正解!


🎯 三分钟掌握泛型核心语法

语法含义类比
func Foo[T any](x T)T 可以是任意类型“请填一个名字” 👉 张三、李四、喵星人
func Max[T constraints.Ordered](a, b T)T 必须能比大小(int/string/float)“请填一个能打分的科目” 👉 数学✅ 英语✅ 早睡❌
type Box[T any] struct { value T }泛型结构体“快递盒”📦:装啥都行,但得提前说好大小

🔔 注意:
constraints.Ordered 是 Go 1.21+ 的标准库约束(旧版用 golang.org/x/exp/constraints
👉 它说:“我只收能 < > == 的类型,谢绝 []byteCat。”


🛠 实战:别再写 MapIntToString 了!

需求:把 []int[]string(比如 [1,2,3]["1","2","3"]

以前(痛苦面具版):

func IntSliceToStringSlice(is []int) []string {
    ss := make([]string, len(is))
    for i, v := range is {
        ss[i] = strconv.Itoa(v)
    }
    return ss
}

现在(泛型优雅版):

func Map[T any, U any](s []T, f func(T) U) []U {
    r := make([]U, len(s))
    for i, v := range s {
        r[i] = f(v)
    }
    return r
}

// 用起来:
nums := []int{1, 2, 3}
strs := Map(nums, strconv.Itoa) // ✅ 编译通过!类型自动推导

🐹 土拨鼠感动落泪:
“我终于不用在 MapIntToStringMapStringToIntMapCatToDog 之间反复横跳了!”


⚠️ 泛型常见“坑位” & 避坑指南

误区现实土拨鼠应对策略
“泛型 = 运行时反射”❌ 编译时单态化(monomorphization)→ 生成具体类型代码享受零运行时开销 🚀
“我能泛型嵌套泛型泛型?”✅ 但别学 Haskell 程序员写 [][][]T深度 >2 时,泡杯茶冷静一下 ☕
[]TT[] 一样吗?”❌ Go 只认 []T(方括号在前!)记住口诀:“切片像煎饼,方的裹圆的” 🥞

🧪 小测验:以下代码能编译吗?

type Container[T any] struct {
    items []T
}

func (c *Container[T]) Add(item T) {
    c.items = append(c.items, item)
}

func main() {
    c := Container[int]{} // 注意:必须带类型参数!
    c.Add(42)
}

能!
❌ 但 Container{}(漏掉 [int])会报错:
cannot infer T → 编译器:“大哥,你到底想装 int 还是 string?给个准话!”

📌 口诀:泛型实例化,方括号不能忘!


🎁 终极赠礼:泛型三原则(土拨鼠版)

  1. 能用 any 就用 any —— 别一上来就写 T constraints.Integer,除非你真需要 <
  2. 类型推导是你的朋友 —— Map(nums, f)Map[int, string](nums, f) 简洁 200%
  3. 当泛型让你头秃 → 先写非泛型版 → 跑通 → 再泛化 → ✅ 安全落地

🐾 结语:
Go 泛型不是银弹,但它是你工具箱里那把瑞士军刀——平时收着,关键时刻咔嗒一开,问题迎刃而解
所以,放下 interface{} 的扳手,拿起泛型的螺丝刀——
你的 CopySlice,值得一次编写,终身复用。

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
如何构建企业级云原生计算基础设施
云原生大数据是大数据平台新一代架构和运行形态。通过升级云原生架构,可以为大数据在弹性、多租户、敏捷开发、降本增效、安全合规、容灾和资源调度等方向上带来优势。本议题将依托字节跳动最佳实践,围绕云原生大数据解决方案进行展开。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论