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,还是两个?

e18d20c94006dfe0-9eef65073f0f6be0-d9c854df534cf3bd0dca74a7e620249c.jpeg

根据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


  目录