Go 泛型:泛型编程如何改变游戏规则

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

一切始于 Go 1.18:为什么泛型不仅仅是一个时髦的特性?

\n\n

长期以来,Go 社区一直以语言的简洁性为傲。缺乏泛型编程被认为是有意为之的选择:“我们不需要复杂的模板,我们编写清晰的代码”。但现实终究占了上风。开发者们厌倦了为 int、string 和 float64 复制粘贴函数——或者使用 interface{} 并随后进行类型转换,从而在编译阶段失去安全性。

\n\n

Go 1.18(2022 年)的发布成为了一场革命。泛型为语言添加了类型参数和约束。现在,Go 程序员可以编写泛型数据结构和算法,而无需牺牲性能和严格的类型安全。

\n\n

在本文中,我们将了解 Go 中泛型的工作原理,查看真实的代码示例,并理解何时应该使用它们,何时不应该。开始吧!

\n\n

用简单的话说,Go 中的泛型是什么?

\n\n

想象一下,你正在编写一个在切片中查找最小元素的函数。没有泛型,你必须创建三个版本:分别用于 int、float64 和 string。或者使用 interface{} 进行类型转换——既慢又不安全。

\n\n

泛型允许你编写一个函数,该函数适用于满足特定条件的任何类型。语法很简单:在函数名后的方括号中指定类型参数。

\n\n

语法:方括号和 any 关键字

\n\n
package main\n\nimport "fmt"\n\n// Min — 用于查找最小值的泛型函数\nfunc Min[T any](a, b T) T {\n    // 错误:运算符 < 未为 any 定义\n    // if a < b {\n    //     return a\n    // }\n    return b\n}
\n\n

上面的代码无法编译,因为 any 是一个空接口(interface{} 的同义词)。编译器不知道该类型是否支持 < 运算符。需要添加一个约束。

\n\n

类型约束:constraints

\n\n

在 Go 中,约束通过接口来定义。你可以使用 golang.org/x/exp/constraints 包中的内置约束,或者编写自己的约束。

\n\n
import (\n    "fmt"\n    "golang.org/x/exp/constraints"\n)\n\n// Min 适用于任何有序类型\nfunc Min[T constraints.Ordered](a, b T) T {\n    if a < b {\n        return a\n    }\n    return b\n}\n\nfunc main() {\n    fmt.Println(Min(10, 20))       // 10\n    fmt.Println(Min(3.14, 2.71))   // 2.71\n    fmt.Println(Min("apple", "banana")) // "apple"\n}
\n\n

请注意:constraints.Ordered 包含所有定义了运算符 <、<=、>、>= 的类型——即 int、float、string。

\n\n

实战示例:从简单到复杂

\n\n

让我们看看 Go 中的泛型如何简化真实代码的编写。

\n\n

1. 泛型栈(Stack)

\n\n

没有泛型,你将不得不存储 []interface{} 并进行类型断言。有了泛型,代码优雅且安全。

\n\n
package main\n\nimport "fmt"\n\n// Stack — 泛型数据结构\ntype Stack[T any] struct {\n    items []T\n}\n\nfunc (s *Stack[T]) Push(item T) {\n    s.items = append(s.items, item)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n    if len(s.items) == 0 {\n        var zero T\n        return zero, false\n    }\n    item := s.items[len(s.items)-1]\n    s.items = s.items[:len(s.items)-1]\n    return item, true\n}\n\nfunc main() {\n    intStack := Stack[int]{}\n    intStack.Push(1)\n    intStack.Push(2)\n    val, ok := intStack.Pop()\n    fmt.Println(val, ok) // 2 true\n\n    strStack := Stack[string]{}\n    strStack.Push("hello")\n                

博客

书籍推荐