Go组件-interface

109 阅读2分钟

go 中的接口变量(interface)其实是用 iface 和 eface 这两个结构体来表示的:

  • iface 表示某一个具体的接口(含有方法的接口)
  • eface 表示一个空接口

结构体如下:

// 非空接口(如:io.Reader)
type iface struct {
   tab  *itab          // 方法表
   data unsafe.Pointer // 指向变量本身的指针
}

// 空接口(interface{})
type eface struct {
   _type *_type         // 接口变量的类型
   data  unsafe.Pointer // 指向变量本身的指针
}

_type

// _type 是 go 里面所有类型的一个抽象,里面包含 GC、反射、大小等需要的细节,
// 它也决定了 data 如何解释和操作。
// 里面包含了非常多信息:类型的大小、哈希、对齐及 kind 等信息
type _type struct {
    size       uintptr // 数据类型共占用空间的大小
    ptrdata    uintptr // 含有所有指针类型前缀大小
    hash       uint32  // 类型 hash 值;避免在哈希表中计算
    tflag      tflag   // 额外类型信息标志
    align      uint8   // 该类型变量对齐方式
    fieldAlign uint8   // 该类型结构体字段对齐方式
    kind       uint8   // 类型编号
    // 用于比较此类型对象的函数
    equal func(unsafe.Pointer, unsafe.Pointer) bool
    // gc 相关数据
    gcdata    *byte
    str       nameOff // 类型名字的偏移
    ptrToThis typeOff
}

itab

// 编译器已知的 itab 布局
type itab struct {
   inter *interfacetype // 接口类型
   _type *_type
   hash  uint32
   _     [4]byte
   fun   [1]uintptr // 变长数组. fun[0]==0 意味着 _type 没有实现 inter 这个接口
}

// 接口类型
// 对应源代码:type xx interface {}
type interfacetype struct {
    typ     _type     // 类型信息
    pkgpath name      // 包路径
    mhdr    []imethod // 接口的方法列表
}
  • itab 实际上定义了 interfacetype_type 之间方法的交集。作用是什么呢?就是用来判断一个结构体是否实现某个接口的。

  • itab 包含了接口的所有方法,这里面的方法是实际类型的子集。

  • itab 里面的方法列表包含了实际类型的方法指针(也就是实际类型的方法的地址),通过这个地址可以对实际类型进行方法的调用。

  • itab 在实际类型没有实现接口的所有方法的时候,生成失败(失败的意思是,生成的 itab 里面的方法列表是空的,在底层实现上是用 fun[0] = 0 来表示