为什么 Go 里没有泛型?

有人知道为什么 Go 中没有对泛型/模板/whatsInAName 的真正支持吗?所以有一个通用的 map,但它是由编译器提供的,而 Go 程序员不能编写自己的实现。在所有关于使 Go 尽可能正交的讨论中,为什么我可以使用泛型类型而不创建一个新类型?

特别是当涉及到函数式编程时,有 lambdas,甚至闭包,但是对于缺乏泛型的静态类型系统,我如何编写像 filter(predicate, list)这样的泛型高阶函数?好的,链表之类的可以用 interface{}牺牲类型安全来完成。

似乎只有在事后才会将泛型添加到 Go 中,如果有的话。我相信 Ken Thompson 会比 Java 实现者做得更好,但是为什么要把泛型排除在外呢?还是它们已经计划好了,只是还没有付诸实施?

33926 次浏览

注意: 泛型在1.18版本中是 添加到围棋


你会在这里找到答案: http://golang.org/doc/faq#generics

为什么 Go 没有泛型类型?

泛型很可能在某些时候被添加。尽管我们理解有些程序员对它们有紧迫感,但我们并不觉得它们很紧迫。

泛型很方便,但它们在类型系统和运行时的复杂性方面付出了代价。我们还没有找到一种设计能够使价值与复杂性成正比,尽管我们还在继续思考这个问题。与此同时,Go 的内置映射和切片,加上使用空接口构造容器(显式拆箱)的能力,意味着在许多情况下可以编写代码来完成泛型所能完成的工作,即使不那么顺利。

这仍然是一个悬而未决的问题。

针对 Go 1.18发行版(于2022年3月15日获释)实现了泛型建议

“开始2”

仿制药的设计是在 Go2的保护伞下开始的,最初是在 https://blog.golang.org/go2draft,在接下来的3年里还有几个规范草案。

1号

拉斯 · 考克斯,围棋老手之一,写了一个 标题为“通用困境”的博客文章,他在里面问道

你想要慢的程序员,慢的编译器和臃肿的二进制文件,还是慢的执行时间?

缓慢的程序员是没有泛型的结果,缓慢的编译器是由类似于泛型的 C + + 造成的,缓慢的执行时间源于 Java 使用的装箱-拆箱方法。

博客中没有提到的第四种可能性是走 C # 路线。像 C + + 那样生成专用代码,但是在运行时需要时生成。我真的很喜欢它,但是 Go 与 C # 非常不同,所以这可能根本不适用..。

我应该提到,使用流行的类似 Java 1.4的 泛型就位技术(转换为 interface{})会遇到与装箱-解箱(装箱-解箱)完全相同的问题(因为这就是我们正在做的事情) ,而且会丢失编译时类型安全性。对于小类型(如 int) ,Go 优化了 interface{}类型,这样转换到接口{}的 int 列表占用了一个连续的内存区域,并且只占用普通 int 的两倍空间。但是,在从 interface{}进行强制转换时,仍然存在运行时检查的开销。参考文献.

所有添加了通用支持的项目(有几个项目都很有意思)都采用 C + + 的编译时代码生成方式。

参数多态是 正在考虑 < strong > Go 2

这种方法将引入 合约的概念,该概念可用于表示对类型参数的约束:

contract Addable(a T) {
a + a // Could be += also
}

这种合同可以这样使用:

func Sum(type T Addable)(l []T) (total T) {
for _, e := range l {
total += e
}
return total
}

这是一个 建议书在这个阶段。


您的 filter(predicate, list)函数可以使用类型参数实现,如下所示:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
for _, e := range l {
if f(e) {
result = append(result, e)
}
}
return result
}

在这种情况下,没有必要约束 T

事实上,根据 这个的报道:

许多人(错误地)得出结论,围棋团队的立场是“围棋永远不会有泛型”相反,我们理解潜在的仿制品,它们使围棋变得更加灵活和强大,也使围棋变得更加复杂。如果我们要添加泛型,我们希望以一种尽可能多的灵活性和功能,尽可能少增加复杂性的方式来完成。

添加并更新@Vinzenz 和@user7610的优秀答案。

虽然还远远不能确定,但经过十多年的努力,它看起来 就像是参数多态的设计 被误称为泛型,将在未来一两年内出现。它 是一个非常困难的问题,找到一个设计,工程内 现有的语言和感觉好像它属于,但伊恩泰勒投资 在这个问题上投入了大量的能量 答案即将揭晓,请参阅 https://evrone.com/rob-pike-interview

“类型参数-草案设计”支持类型参数的使用,您可以在其中读取处理传入参数的函数,而不依赖于函数声明中指定的类型。参见 https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md

例如,PrintSlice函数接收一片整数或字符串并打印它。

package main


import "fmt"


func PrintSlice(type T)(s []T) {
for _, v := range s {


fmt.Print(v)
}
}


func main() {
PrintSlice([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
PrintSlice([]string{"a", "b", "c", "d"})
}

您可以在这里测试这个示例 https://go2goplay.golang.org/p/I09qwKNjxoq。这个操场的工作方式和通常的 Go 操场一样,但是它支持通用代码。见 https://blog.golang.org/generics-next-step

参数多态的基本意思是“这个函数或数据结构与任何类型的函数或数据结构工作方式相同”。这就是我们所说的泛型。例如,数组的长度不取决于数组中的内容。参见 https://news.ycombinator.com/item?id=23560798

最早将泛型添加到 Go 中的是 Go 1.17版本,计划于2021年8月发布。参见 https://blog.golang.org/generics-next-step