Golang-Interface底层是怎么实现的?


引言

  • 从runtime包分析学习interface底层结构和interface断言的实现、

是nil,但不完全是nil

func InterfaceTest(v interface{}) {
    fmt.Println(v)
    if v == nil {
        fmt.Println("v is nil")
        return
    }
    fmt.Println("v is not nil")
}

func InterfaceTest2(v *int) {
    fmt.Println(v)
    if v == nil {
        fmt.Println("v is nil")
        return
    }
    fmt.Println("v is not nil")
}

func main() {
    var v *int = nil
    InterfaceTest(v)
    fmt.Println("")
    InterfaceTest2(v)
}

运行main方法,查看输出结果发现:InterfaceTest2是符合预期的。从InterfaceTest的输出来看,v是nil,但不完全为nil,经过interface转换之后发生了什么?

<nil>
v is not nil

<nil>
v is nil

interface底层结构

interface判断与预期不一样的根本原因,interface根本不是那么简简单单,而是两个struct。什么?interface不是单纯的值?是struct,还是两个?

根据interface是否包含method,底层实现上有两种struct:eface,iface

  • eface表示不包含任何方法的空接口,也称为 empty interface。

    type eface struct {
      _type *_type // 指向类型信息
      data  unsafe.Pointer // 指向值信息
    }
    type _type struct {
      size       uintptr // 类型的大小
      ptrdata    uintptr // 所有指针的内存前缀大小
      hash       uint32  // 类型的hash值。此处提前计算好,可以避免在哈希表中计算
      tflag      tflag   // 额外的类型信息标志。此处为类型的 flag 标志,主要用于反射
      align      uint8   // 对应变量与该类型的内存对齐大小。
      fieldalign uint8   // 对应类型的结构体的内存对齐大小。
      kind       uint8   // 类型的枚举值。包含 Go 语言中的所有类型,例如:kindBool、kindInt、kindInt8、kindInt16 等。
      alg        *typeAlg  // algorithm table
      gcdata    *byte    // 存储垃圾收集器的 GC 类型数据
      str       nameOff  // string form
      ptrToThis typeOff  // type for pointer to this type, may be zero
    }
  • iface包含一些method的具体实现,存在itab.fun变量里

    type iface struct {
      tab  *itab
      data unsafe.Pointer
    }
    type itab struct {
      inter  *interfacetype // 接口的类型信息
      _type  *_type // 具体类型信息,这个type和eface里的type是一个东西
      link   *itab
      bad    int32
      inhash int32      // has this itab been added to hash?
      fun    [1]uintptr // 底层数组,存储接口的方法集的具体实现的地址,其包含一组函数指针,实现了接口方法的动态分派,且每次在接口发生变更时都会更新
    }
    type interfacetype struct {
     typ     _type
     pkgpath name
     mhdr    []imethod
    }
    type imethod struct {
     name nameOff
     ityp typeOff
    }

类型断言是怎么实现的?

  • 断言分为安全和非安全两种
    func myAssert(v interface{}) {
    // 进行变量断言,若不判断容易出现 panic
      s := v.(string)
      // 进行安全断言
    s, ok := v.(string)
    }
    func myAssert(v interface{}) {
    // 进行 switch 断言
    switch v.(type) {
    case string:
        // do something
    case int:
        // do something
    case float64:
        // do something
    }
    }
    iface和eface对应不同的方法,安全和不安全也对应两种方法。(runtime包iface.go)
    func assertI2I(inter *interfacetype, i iface) (r iface) {
      tab := i.tab
      if tab == nil {
          // explicit conversions require non-nil interface value.
          panic(&TypeAssertionError{nil, nil, &inter.typ, ""})
      }
      if tab.inter == inter {
          r.tab = tab
          r.data = i.data
          return
      }
      r.tab = getitab(inter, tab._type, false)
      r.data = i.data
      return
    }
    func assertI2I(inter *interfacetype, i iface) (r iface)
    func assertE2I2(inter *interfacetype, e eface) (r iface, b bool)
    func assertE2I(inter *interfacetype, e eface) (r iface)

参考

http://legendtkl.com/2017/07/01/golang-interface-implement/

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


评论
  目录