推荐阅读:【Golang】类型系统
在编译期间,编译器知道每种类型定义得方法,方法本质是函数,所以在方法得调用上,接收者作为第一个参数传入。这也是为什么 t.F1()==F1(t)
等价得原因。
到了执行阶段,为了支持反射,接口动态派发,类型断言这些语言特性或机制,编译器会给每种类型生成对应得类型描述信息写入执行文件,这些类型描述信息就是类型元数据。
以下都是 Go
语言的内置类型:
- 内置字符串类型:
string
。 - 内置布尔类型:
bool
. - 内置数值类型:
int8
、uint8
(byte
)、int16
、uint16
、int32
(rune
)、uint32
、int64
、uint64
、int
、uint
、uintptr
。float32
、float64
。complex64
、complex128
。
注:byte
是uint8
的一个内置别名,rune
是int32
的一个内置别名。
我们也可以自己定义类型。
//可以给自定义类型定义方法。
type T1 int
//可以给自定义类型定义方法。
type T2 struct {
name string
}
//接口类型是无效的方法接收者
type T3 interface {
F1()
}
数据类型很多,不管是内置类型还是自定义类型,他的类型元数据是全局唯一的,这些类型元数据共同构成了 Go
语言的类型系统。
那么开始正题吧!
每个类型元数据基础信息都放到了 runtime._type
结构体中,作为每个类型元数据的 Header
。
type _type struct {
size uintptr //大小
ptrdata uintptr //含有所有指针类型前缀大小
hash uint32 //类型
tflag tflag //类型的特征标记
align uint8 //作为整体变量存放时的对齐字节数
fieldalign uint8 //当前结构字段的对齐字节数
kind uint8 //基础类型枚举值和反射中的 Kind 一致,kind 决定了如何解析该类型
alg *typeAlg //指向一个函数指针表,该表有两个函数,一个是计算类型 Hash 函数。另一个是比较两个类型是否相同的 equal 函数
gcdata *byte //GC 相关
str nameOff //类型名称字符串在二进制文件段中的偏移量
ptrToThis typeOff //类型元信息指针在二进制文件段中的偏移量
}
_type
存储基本信息,还有额外需要描述的信息也需要存储。例如 slice
的类型元数据在 _type
结构体后面记录了一个 *_type
,指向其存储元素的类型元数据。
type slicetype struct {
typ _type
elem *_type //指向存储元素的类型元数据,如果存储string类型,那么这个指针指向 string 类型的元数据
}
指针类型也是如此。
type ptrtype struct {
typ _type
elem *_type //指向 指针类型指向的类型元数据 例如 *int 那么elem指向 int 类型的元数据
}
有些内置类型就没有这么简单。例如 map
,struct
。
type maptype struct {
typ _type
key *_type
elem *_type
bucket *_type // internal type representing a hash bucket
// function for hashing keys (ptr to key, seed) -> hash
hasher func(unsafe.Pointer, uintptr) uintptr
keysize uint8 // size of key slot
elemsize uint8 // size of elem slot
bucketsize uint16 // size of bucket
flags uint32
}
type structtype struct {
typ _type
pkgPath name
fields []structfield
}
但是基本结构都是如此。可以来 type.go 来看看内置类型的元数据
如果是自定义类型,那么类型元数据的结构体后面还会有一个 uncommontype
结构体。
type uncommontype struct {
pkgpath nameOff //记录类型所在的包路径;
mcount uint16 // 记录了该类型关联到多少个方法;
_ uint16 // unused
moff uint32 //记录的是这些方法的元数据组成的数组,相对于这个uncommontype结构体偏移了多少字节。
}
//方法元数据
type method struct {
name nameOff // 方法名称偏移量
mtyp typeOff //方法的类型元数据
ifn textOff //fn used in interface call (one-word receiver)
tfn textOff //fn used for normal method call
}
举个例子🌰:
type myslice []string //定义了一个新的类型 myslice
//定义的两个方法
func (ms myslice) Len(){
fmt.Println(len(ms))
}
func (ms myslice) Cap(){
fmt.Println(cap(ms))
}
myslice
类型元数据由以下构成:
slicetype
类型描述信息uncommontype
结构体
注:我们可以通过 uncommontype
结构体找到 myslice
的方法元数据列表。
在内置数据里面,byte
是uint8
的一个内置别名,rune
是int32
的一个内置别名。
以下有两种方法,他们有什么不同呢?
type MyType1 = int32
type MyType2 int32
- 第一种方式叫做类型
int32
取别名。实际上MyType1
和int32
会关联到同一个类型元数据,属于同一种类型。
- 第二种方式属于创建新类型,拥有自己的类型元数据,
MyType2
和int32
是两个不同的类型,对应元数据也会不同。