Skip to content

Latest commit

 

History

History
164 lines (126 loc) · 5.84 KB

类型系统.md

File metadata and controls

164 lines (126 loc) · 5.84 KB

类型系统

推荐阅读:【Golang】类型系统

深入研究 Go interface 底层实现

前言

在编译期间,编译器知道每种类型定义得方法,方法本质是函数,所以在方法得调用上,接收者作为第一个参数传入。这也是为什么 t.F1()==F1(t) 等价得原因。

到了执行阶段,为了支持反射,接口动态派发,类型断言这些语言特性或机制,编译器会给每种类型生成对应得类型描述信息写入执行文件,这些类型描述信息就是类型元数据

以下都是 Go 语言的内置类型:

  • 内置字符串类型:string
  • 内置布尔类型:bool.
  • 内置数值类型:
    • int8uint8byte)、int16uint16int32rune)、uint32int64uint64intuintuintptr
    • float32float64
    • complex64complex128

注:byteuint8的一个内置别名,runeint32的一个内置别名。

我们也可以自己定义类型。

//可以给自定义类型定义方法。
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 类型的元数据
}

有些内置类型就没有这么简单。例如 mapstruct

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 的方法元数据列表。

图片

alias 别名

在内置数据里面,byteuint8的一个内置别名,runeint32的一个内置别名。

以下有两种方法,他们有什么不同呢?

type MyType1 = int32
type MyType2 int32
  • 第一种方式叫做类型 int32 取别名。实际上 MyType1int32 会关联到同一个类型元数据,属于同一种类型。

图片

  • 第二种方式属于创建新类型,拥有自己的类型元数据,MyType2int32 是两个不同的类型,对应元数据也会不同。

图片