【发布时间】:2021-08-15 13:14:22
【问题描述】:
我们知道,我们可以声明所有类型的 map 和 slice,比如 []string, []int, []interface{}
这已经是某种泛型了。
不知道它是如何实现的?
是不是就像interface{}一样,存储type和value,就像用void*在c语言中实现Generic一样?
【问题讨论】:
-
我猜编译器会生成一些机器。以地图为例,你可以看到
mapaccess接收到一组非常特殊的参数,并输出一个unsafe.Pointer cs.opensource.google/go/go/+/refs/tags/go1.16.7:src/runtime/… -
处理类型修饰符“slice”(注意 slice 本身不是类型,对于某些类型 T,它始终是“T 的切片”,这就是它是“修饰符”的原因) 在编译器内部。编译器将
[]T视为类型,并在需要函数调用时插入适当的必要函数调用,或在适合内联代码时插入内联代码。这与直接在编译器中处理intvsuint64vsfloat32相同:我们确保生成正确的代码。这就是语言的全部内容。 -
在实现方面,事情变得更加有趣:为了避免编写多个不同的地图访问器、切片构建器等,我们希望运行时系统是能够访问编译器为运行时系统留下的信息。因此,运行时系统本身与编译器本身绑定(弱绑定或强绑定,具体取决于我们希望将其绑定到何种程度)。在像 C++ 这样的“成熟”语言中,我们倾向于发现 CPU 制造商定义 ABI,以便 C++ 编译器可以调用英特尔提供的库。
-
在快速发展的语言中,比如 Go 已经有好几年了,ABI 往往不太常见,运行时通常与特定的编译器版本更紧密地联系在一起。这使得提供新的更快的编译器变得更容易(编译速度更快,或运行速度更快,以更可取的为准),即使这意味着您不能将新编译器与现有的编译目标代码一起使用。
-
Go 构建系统本身鼓励“整个源代码随时可访问”的编译模型,这进一步避免了 C++ ABI 倾向于产生的那种僵化。不过,每种方法都有优缺点。
标签: go