golang 类型系统

类型系统



类型系统

如果我们自定义一个结构体类型T,并给它关联一个方法F1。我们知道变量t的内存布局只包含一个字符串变量,那它和F1之间怎样建立关联?

type T struct {
    name string
}
func (t T) F1() {
    fmt.Println(t.name)
}  
func main() {
    t := T{name: "eggo"}
    t.F1()
}

由于编译阶段,编译器知道每种类型定义了哪些方法,之前我们也介绍过,方法本质上就是函数,只不过在调用时,接收者会作为第一个参数传入。所以通过变量t来调用方法F1时,编译器自然知道要调用哪个函数。

在这里插入图片描述

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

在Go语言里,int、float、string、slice、map、interface等属于内置类型(built-in),而我们自己通过下面这三种方式定义的类型,都属于自定义类型。

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
    alg        *typeAlg
    gcdata     *byte
    str        nameOff
    ptrToThis  typeOff
}

在_type之后存储的是各类型额外需要描述的信息,例如slice的类型元数据在_type结构体后面,记录着一个*_type,指向其存储的元素的类型元数据。

type slicetype struct {
    typ   _type
    elem  *_type
}

如果是存储string的slice类型,这个指针就指向string类型的元数据。

在这里插入图片描述

再看看指针类型的元数据,它在_type后面也额外存储了一个*_type,指向指针类型指向的那个类型的元数据。

type ptrtype struct {
    typ   _type
    elem  *_type
}

在这里插入图片描述

内置类型的元数据都是这样的结构,可以到runtime包下的type.go查看struct、map等类型的元数据信息。

自定义类型元数据

然而如果是自定义类型,这后面还会有一个uncommontype结构体。

type uncommontype struct {
    pkgpath nameOff
    mcount  uint16 
    _       uint16 // unused
    moff    uint32 
}
  • pkgpath 记录类型所在的包路径;
  • mcount 记录了该类型关联到多少个方法;
  • moff 记录的是这些方法的元数据组成的数组,相对于这个uncommontype结构体偏移了多少字节。方法元数据结构如下:
type method struct {
    name nameOff
    mtyp typeOff
    ifn  textOff
    tfn  textOff
}

例如,我们基于[]string定义一个新类型myslice,它就是一个自定义类型,可以给它定义两个方法Len和Cap。

myslice的类型元数据中,首先是slicetype类型描述信息,然后在后面加上uncommontype结构体。注意通过uncommontype这里记录的信息,我们就可以找到myslice的方法元数据列表了。

在这里插入图片描述

alias

现在我们可以利用类型元数据来描述下面这两种写法的不同了:

type MyType1 = int32
type MyType2 int32

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

在这里插入图片描述

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

在这里插入图片描述

既然每种类型都有唯一对应的类型元数据,而类型定义的方法能通过类型元数据找到,那么,很多问题就变得好解释了,例如接下来的“接口”。

文章转载于:

https://mp.weixin.qq.com/s/qQsLP5o7U8AG_phzGzbjJg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值