这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会,此处应有掌声!
文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中。在看文章的时候,对照仓库中代码学习效果更佳!
前面文章空接口和非空接口,它们都属于接口,空接口底层是由runtime.eface
结构体实现,而非空接口底层是由runtime.iface
结构体实现。
eface
即empty interface
(空接口)。
eface结构体位于runtime包runtime2.go
文件中。
type eface struct {
_type *_type
data unsafe.Pointer
}
因为空接口不包含任何方法,所以它的结构也很简单,只包含:
这个结构体是golang中的变量类型的基础,所以空接口可以指定任意变量类型。
示例:
func TestEface(t *testing.T) {
b := Binary(200)
t.Log(b)
any := (interface{})(b)
t.Log(any)
}
下面具体讲下_type
_type
是Go类型的运行时表示。它包含了很多类型的元信息,例如:类型的大小、哈希、对齐以及种类等。
_type
可以认为是Go语言中所有类型的公共描述,Go语言中几乎所有的数据结构都可以抽象成_type,是所有类型的表现,可以说是万能类型。
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
// gcdata stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte
str nameOff
ptrToThis typeOff
}
iface结构体位于runtime包runtime2.go
文件中。
type iface struct {
tab *itab
data unsafe.Pointer
}
和eface相同的是都有指向具体数据的指针data
字段。
不同的是_type变成了tab。
// 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.
}
itab结构体也包含_type类型的字段。
interfacetype
,顾名思义就是接口类型。type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod
}
在go中除了interfacetpe 外,还有类似的一些类型,如 arraytype、maptype、chartype、chantype 和 slicetype
等,它们都可以在 src/runtime/type.go 文件里找到。
Go语言各种数据类型都是在 _type 字段的基础上,增加一些额外的字段来进行管理的,比如:
type arraytype struct {
typ _type
elem *_type
slice *_type
len uintptr
}
type chantype struct {
typ _type
elem *_type
dir uintptr
}
type slicetype struct {
typ _type
elem *_type
}
type functype struct {
typ _type
inCount uint16
outCount uint16
}
你可能会觉得奇怪,为什么 fun 数组的大小为1,要是接口定义了多个方法可怎么办?实际上,这里存储的是第一个方法的函数指针。如果 fun[0] 为 0 时,说明 _type 并没有实现该接口。否则表示已经实现,如果有更多的方法,在它之后的内存空间里继续存储。从汇编角度来看,通过增加地址就能获取到这些函数指针,没什么影响。顺便提一句,这些方法是按照函数名称的字典序进行排列的。