Go语言中的类型系统

golang type

Posted by alovn on February 9, 2021

类型元数据

在Go语言中像byte、int8、int16、int32、int64、string、slice、func、map…等等,这些属于内置类型,而我们自己通过 type T struct 定义的类型属于自定义类型。

数据类型虽然多,但是不管是内置类型还是自定义类型,都有对应的类型描述信息,称为它的『类型元数据』,而且每种类型的元数据都是全局唯一的,这些类型元数据共同构成了Go语言的类型系统。

像类型名称、类型大小、对齐边界、是否为自定义类型等,是每个类型元数据都要记录的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//src/runtime/type.go
type _type struct {
    size       uintptr
    ptrdata    uintptr // size of memory prefix holding all pointers
    hash       uint32
    tflag      tflag
    align      uint8
    fieldAlign uint8
    kind       uint8
    equal func(unsafe.Pointer, unsafe.Pointer) bool
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}

它被放到了runtime._type结构体中,作为每个类型元数据的Header。在_type之后存储的是各种类型额外需要描述的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
}
type ptrtype struct {
    typ  _type
    elem *_type
}

type structtype struct {
    typ     _type
    pkgPath name
    fields  []structfield
}

type interfacetype struct {
    typ     _type
    pkgpath name
    mhdr    []imethod
}

如果是自定义的类型,后面还会有一个uncommentype结构体:

1
2
3
4
5
6
7
type uncommontype struct {
    pkgpath nameOff //记录类型所在包路径
    mcount  uint16 // 记录该类型关联到多少个方法
    xcount  uint16 // number of exported methods
    moff    uint32 // 记录的是这些方法元数据组成的数组相对于这个uncommentype结构体偏移了多少字节
    _       uint32 // unused
}

例如基于[]string定义的一个新的自定义类型myslice:

1
2
3
4
5
6
7
8
type myslice []string

func(ms myslice) Len() {
    fmt.Println(len(ms))
}
func(ms myslice) Cen() {
    fmt.Println(cap(ms))
}

myslice的类型元数据首先是[]string类型的描述信息,然后在后面加上uncommontype结构体:

1
2
3
4
5
//myslice结构体
struct {
    slicetype
    uncommontype
}

通过uncommontype这里记录的信息,就可以找到myslice定义的方法元数据在哪儿了。如果uncommontype的地址为addrA加上moff字节的偏移就是myslice关联的方法元数据数组。

写法区别

再来看下这两种写法的区别:

1
2
    type MyType1 = int32
    type Mytype2 int32

MyType1这种写法叫做给类型int32取别名,实际上MyType1和int32会关联到同一个元数据,属于同一种类型,rune 和 int32 就是这样的关系。

1
MyType1 ——————> int32类型元数据 <—————— int32

而MyType2这种写法属于基于已有类型创建的新类型,MyType2会自立门户,拥有自己的类型元数据。即使MyType2相对于int32来说并没有做任何改变,它们两个对应的类型元数据也已经不同了。

1
2
3
MyType2 ——————> MyType2类型元数据

int32 ——————> int32类型元数据