课程章节:4-9 ,4-10接口--隐式更好还是显式更好
课程讲师:Moody
课程内容:
※ go 接口底层
△ iface: 用于表示拥有方法的接口
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
// 接口自身信息
inter *interfacetype
// 接口类型变量动态类型信息
_type *_type
//
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
// fun是动态类型已经实现的接口方法的调用地址数据
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
△ 如果是空接口,也就是interface{} 则底层是 eface
type eface struct {
_type *_type
data unsafe.Pointer
}
※ 结构体和指针实现接口
这是因为,在使用结构体实现一个方法的时候,go自动的也用结构体的指针实现了一个方法。但是如果你只是使用指针实现,go不会再用结构体实现一遍。
※ 当且仅当两个接口类型变量的类型信息相同,且数据指针所指向的数据相同时,两个接口类型才算相等,==为true
※通过println可输出接口类型变量的两部分指针变量的值
※ convT2E 是将任意类型转换为eface,而convT2I是将任意类型转换为iface。主要思路就是根据传入的类型信息分配一块内存空间,并将elem指向的数据复制到这个新分配的内存空间,最后传入的类型信息作为返回值结构体中的类型信息,返回值结构中的数据指针指向新分配的那个内存空间。
※那么go是如何知道传入的T的类型的,这是go的编译器的工作,编译器知道每个要转换为接口类型变量的动态类型变量的类型(反射) 具体源码在 /src/cmd/compile/internal/gc/walk.go -> func walkexpr()
从上面就可以看出,为了处理空接口,是需要额外开辟一个内存空间的,等于说元数据成为了2被的内存空间,这也是大多数的人在呼吁尽量少的使用interface{}。或者,一些github的项目宣称自己没有使用interface的。毕竟内存不仅仅是分配一块出来,还容易逃逸。
课程收获:
明白了go的"泛型" interface的一些底层原理