Go 中的泛型:改变代码可重用性
Go 1.18 中引入的泛型彻底改变了编写可重用和类型安全代码的方式。泛型带来了灵活性和强大的功能,同时保持了 Go 的简单哲学。然而,要了解细微差别、优点以及泛型与传统方法(如 interface{} )的比较,需要仔细观察。
让我们探索泛型的复杂性,深入研究约束,将泛型与接口{}进行比较,并演示它们的实际应用。我们还将讨论性能考虑因素和二进制大小的影响。让我们开始吧!
什么是泛型?
泛型允许开发人员编写可以在任何类型上操作的函数和数据结构,同时保持类型安全。泛型不依赖于涉及运行时类型断言的接口{},而是让您指定一组约束来规定类型上允许的操作。
语法
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
T: 类型参数,表示类型的占位符。
TypeConstraint:将 T 的类型限制为特定类型或一组类型。
parameterName T:参数使用泛型类型T.
ReturnType:函数还可以返回 T.
类型的值示例
func Sum[T int | float64](a, b T) T { return a + b }
func Sum: 声明函数名称 Sum
[T int | float64]: 指定引入 T 作为类型参数的类型参数列表,约束为特定类型(int 或 float64)。 Sum 函数只能采用 int 或 float64 参数,不能组合使用,两者都必须是 int 或 float64。我们将在下面的部分中进一步探讨这一点。
(a, b T): 声明两个参数 a 和 b,均为 T 类型(泛型类型) ).
T: 指定函数的返回类型,与类型参数 T.
匹配约束:泛型的构建块
约束定义哪些操作对于泛型类型有效。 Go 提供了强大的约束工具,包括实验性约束包(golang.org/x/exp/constraints)。
内置约束
Go 引入了带有泛型的内置约束,以提供类型安全,同时允许灵活地定义可重用和泛型代码。这些约束使开发人员能够对泛型函数或类型中使用的类型强制执行规则。
Go 有以下内置约束
- any:代表任意类型。它是 interface{} 的别名。当不需要约束时使用
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
- comparable:允许支持相等比较(== 和 !=)的类型。对于映射键、重复检测或相等检查很有用。这不能用于映射、切片和函数,因为这些类型不支持直接比较。
func Sum[T int | float64](a, b T) T { return a + b }
实验限制
- constraints.Complex:允许复杂数字类型(complex64 和complex128)。
- constraints.Float:允许浮点数值类型(float32 和 float64)
- constraints.Integer:允许任何有符号和无符号整数(int8、int16、int32、int64、int、uint8、uint16、uint32、uint64 和 uint)
- constraints.Signed:允许任何有符号整数(int8、int16、int32、int64 和 int)
- constraints.Unsigned:允许任何无符号整数(uint8、uint16、uint32、uint64 和 uint)。
- constraint.Ordered:允许进行比较的类型(<.>、>=),支持所有数字类型和字符串(int、float64、string 等)。
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
自定义约束
自定义约束是定义泛型类型参数必须满足的一组类型或类型行为的接口。通过创建您自己的约束,我们可以;
将类型限制为特定子集,例如数字类型。
需要类型来实现特定的方法或行为。
为您的通用函数和类型添加更多控制和特异性。
语法
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
示例
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
Sum 函数 只能使用 int、int64 和 float64 参数调用。
方法的限制
如果你想强制类型必须实现某些方法,你可以使用这些方法来定义它。
type Numeric interface { int | float64 | uint }
Formatter 约束要求任何用作 T 的类型必须具有返回 字符串.
组合约束自定义约束可以结合类型集和方法要求
type Number interface { int | int64 | float64 } func Sum[T Number](a, b T) T { return a + b }
int、float54),并且需要存在 abs 方法。
泛型与接口{}在引入泛型之前,使用interface{}来实现灵活性。然而,这种方法有局限性。
类型安全
- interface{}:依赖运行时类型断言,增加运行时出错的几率。
- 泛型:提供编译时类型安全性,在开发过程中尽早捕获错误。
表现
接口{}:由于额外的运行时类型检查,速度较慢。
泛型:更快,因为编译器会生成特定于类型的优化代码路径。
代码可读性
界面{}:通常冗长且不太直观,使代码更难维护。
泛型:更简洁的语法可以带来更直观和可维护的代码。
二进制大小
interface{}:生成更小的二进制文件,因为它不会为不同类型重复代码。
泛型:由于类型专门化而略微增加二进制大小以获得更好的性能。
示例
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
代码运行良好,类型断言是开销。 Add 函数可以使用任何参数调用,a 和 b 参数可以是不同的类型,但是代码会在运行时崩溃。
func Sum[T int | float64](a, b T) T { return a + b }
泛型消除了由不正确的类型断言引起的运行时恐慌的风险并提高了清晰度。
表现
泛型为每种类型生成专门的代码,与接口{}相比,可以带来更好的运行时性能。
二进制大小
存在一个权衡:泛型由于每种类型的代码重复而增加了二进制大小,但这与好处相比通常可以忽略不计。
Go 泛型的局限性
约束的复杂性: 虽然像 Constraints.Ordered 这样的约束简化了常见用例,但定义高度自定义的约束可能会变得冗长。
结构体中没有类型推断: 与函数不同,您必须为结构体显式指定类型参数。
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
仅限于编译时约束:Go 泛型专注于编译时安全,而 Rust 这样的语言使用生命周期和特征提供更强大的约束。
让我们进行基准测试——做得比说的更好
我们将实现一个具有接口{}和通用的简单队列,并对结果进行基准测试。
接口{}队列实现
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
通用队列实现
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
type Numeric interface { int | float64 | uint }
结果分析
执行时间:
通用实现比 interface{} 版本快大约 63.64%,因为它避免了运行时类型断言并直接对给定类型进行操作。分配:
Interface{} 版本的分配量增加了 3 倍,这主要是由于插入和检索值时的装箱/拆箱。这增加了垃圾收集的开销。
对于较大的工作负载,例如 100 万次入队/出队操作,性能差距会扩大。具有高吞吐量要求的实际应用程序(例如消息队列、作业调度程序)可以从泛型中受益匪浅。
最后的想法
Go 中的泛型在功能强大和简单性之间取得了平衡,为编写可重用和类型安全的代码提供了实用的解决方案。虽然不像 Rust 或 C 那样功能丰富,但与 Go 的极简主义哲学完美契合。了解约束等约束。有效有序和利用泛型可以极大地提高代码质量和可维护性。
随着泛型的不断发展,它们注定会在 Go 的生态系统中发挥核心作用。因此,潜入、实验并拥抱 Go 编程类型安全性和灵活性的新时代!
查看 github 存储库以获取一些关于泛型的示例。
萨达南多达瓦达卡尔
/
Go泛型
存储库包含 go 泛型的工作示例
Go 泛型:综合示例存储库
欢迎来到Go 泛型存储库!该存储库是 1.18 版本中引入的用于理解、学习和掌握 Go 中泛型的一站式资源。泛型为 Go 带来了类型参数的强大功能,使开发人员能够编写可重用且类型安全的代码,而不会影响性能或可读性。
该存储库包含精心策划的示例,涵盖广泛的主题,从基本语法到高级模式和实际用例。无论您是初学者还是经验丰富的 Go 开发人员,此集合都将帮助您在项目中有效地利用泛型。
?里面有什么
?基本通用程序
这些示例介绍了泛型的基本概念,帮助您掌握语法和核心功能:
- GenericMap:演示通用映射函数来转换任何类型的切片。
- 交换:一般交换两个值的简单而强大的示例。
- FilterSlice:显示如何过滤...
以上是Go 中的泛型:改变代码可重用性的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Go语言在构建高效且可扩展的系统中表现出色,其优势包括:1.高性能:编译成机器码,运行速度快;2.并发编程:通过goroutines和channels简化多任务处理;3.简洁性:语法简洁,降低学习和维护成本;4.跨平台:支持跨平台编译,方便部署。

Golang在并发性上优于C ,而C 在原始速度上优于Golang。1)Golang通过goroutine和channel实现高效并发,适合处理大量并发任务。2)C 通过编译器优化和标准库,提供接近硬件的高性能,适合需要极致优化的应用。

Golang和C 在性能竞赛中的表现各有优势:1)Golang适合高并发和快速开发,2)C 提供更高性能和细粒度控制。选择应基于项目需求和团队技术栈。

Golang在性能和可扩展性方面优于Python。1)Golang的编译型特性和高效并发模型使其在高并发场景下表现出色。2)Python作为解释型语言,执行速度较慢,但通过工具如Cython可优化性能。

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。 Golang以其并发模型和高效性能着称,Python则以简洁语法和丰富库生态系统着称。

C 更适合需要直接控制硬件资源和高性能优化的场景,而Golang更适合需要快速开发和高并发处理的场景。1.C 的优势在于其接近硬件的特性和高度的优化能力,适合游戏开发等高性能需求。2.Golang的优势在于其简洁的语法和天然的并发支持,适合高并发服务开发。

GoimpactsdevelopmentPositationalityThroughSpeed,效率和模拟性。1)速度:gocompilesquicklyandrunseff,ifealforlargeprojects.2)效率:效率:ITScomprehenSevestAndArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdArdEcceSteral Depentencies,增强开发的简单性:3)SimpleflovelmentIcties:3)简单性。

Golang和C 在性能上的差异主要体现在内存管理、编译优化和运行时效率等方面。1)Golang的垃圾回收机制方便但可能影响性能,2)C 的手动内存管理和编译器优化在递归计算中表现更为高效。
