前言

原文:A gentle introduction to generics in Go byDominik Braun

万俊峰Kevin:我看了觉得文章非常简单易懂,就征求了作者同意,翻译出来给大家分享一下。

本文是对泛型的基本思想及其在 Go 中的实现的一个比较容易理解的介绍,同时也是对围绕泛型的各种性能讨论的简单总结。首先,我们来看看泛型所解决的核心问题。

问题

tree

这在大多数情况下都很好用,但它也有一些缺点。

interface{}value

这样并不可能在编译时限制类型,像上面这样的类型判断在许多 Go 库中都是很常见的做法。这里有 go-zero 项目中的例子。

int
interface{}

解决方法

T
T
TNodeT
NodeNode[int]Tint

类型约束

T
T
Tcomparable==
interface
Numeric

重获类型安全

interface{}TTT
T
n.ValueTTTint

在编译时恢复类型安全使 Go 代码更可靠,更不容易出错。

泛型使用场景

Ian Lance Taylor
slicesmapschannelslinked listtree
Node
Read(r io.Reader)Read[T io.Reader](r T)

性能

要了解泛型的性能及其在 Go 中的实现,首先需要了解一般情况下实现泛型的两种最常见方式。

这是对各种性能的深入研究和围绕它们进行的讨论的简要介绍。你大概率不太需要关心 Go 中泛型的性能。

虚拟方法表

Virtual Method Table
Virtual Method Table
Virtual Method TableVirtual Method Table

单态化

Monomorphization
intmax
MonomorphizationVirtual Method Table

Go 的实现

这两种方法中哪一种最适合 Go?快速编译很重要,但运行时性能也很重要。为了满足这些要求,Go 团队决定在实现泛型时混合两种方法。

Monomorphizationintfloat64Node"值类型"
Virtual Method Table

结论

MonomorphizationVirtual Method Table

在性能讨论中经常被忽略的是,所有这些好处和成本只涉及到函数的调用。通常情况下,大部分的执行时间是在函数内部使用的。调用方法的性能开销可能不会成为性能瓶颈,即使是这样,也要考虑先优化函数实现,再考虑调用开销。