type iface struct {
tab *itab
data unsafe.Pointer
}
type eface struct {
_type *_type
data unsafe.Pointer
}
eface 对应 interface{}
接口,这个接口就是没有任何的限制,所以结构就非常简单,只需要保存原有数据的指针和对应的类型就可以了。
:::warning
一个接口类型转化为 **interface{}**
,eface 中的 _type 是这个接口类型,还是原有的类型呢?
:::
iface 相比 eface 就复杂一些,但是也只是将类型换成了 itab,itab 的类型如下所示
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptabs.
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
再学习接口的实现之前,先问几个问题:
- 如何通过接口动态的找到对应的方法
- 接口是如何实现类型之间的转换的
- 接口与接口之间的转换是怎么做的
:::danger
Golang 中的接口是如何做到隐式的?
Go 在编译时进行类型检查,所以隐式接口转换时编译器的工作,这里和运行时没有关系。
:::
Go 语言的编译器会在编译期间将一些需要动态派发的方法调用改写成对目标方法的直接调用,以减少性能的额外开销。如果在这里禁用编译器优化,就会看到动态派发的过程
type2interface
interface 的 itab 类型主要在编译期就生成了,运行时只是将其指针拷贝到栈上
图一:pointer type
图二:struct type
相对于指针类型直接将 data 和 itab 的指针传输到栈上,结构体就转换为 interface 就比较复杂了,这里需要调用 convT2I()
函数,这里主要是为了将数据从栈上拷贝到堆上,这里是为了防止数据被栈释放,导致悬垂指针。