zoukankan      html  css  js  c++  java
  • 深入理解golang: interface源码分析

    分析接口的赋值,反射,断言的实现原理

    版本:golang v1.12


    interface底层使用2个struct表示的:efaceiface

    一:接口类型分为2个#

    1. 空接口#

    Copy
    //比如
    var i interface{}
    

    2. 带方法的接口#

    Copy
    //比如
    type studenter interface {
        GetName() string
        GetAge()  int
    }
    

    二:eface 空接口定义#


    空接口通过eface结构体定义实现,位于src/runtime/runtime2.go

    Copy
    type eface struct {
    	_type *_type //类型信息
    	data  unsafe.Pointer //数据信息,指向数据指针
    }
    


    可以看到上面eface包含了2个元素,一个是_type,指向对象的类型信息,一个 data,数据指针

    三:_type 结构体#


    _type 位于 src/runtime/type.go


    _type 是go里面所有类型的一个抽象,里面包含GC,反射,大小等需要的细节,它也决定了data如何解释和操作。
    里面包含了非常多信息 类型的大小、哈希、对齐以及种类等自动。


    所以不论是空eface和非空iface都包含 _type 数据类型

    Copy
    type _type struct {
    	size       uintptr //类型大小
    	ptrdata    uintptr //含有所有指针类型前缀大小
    	hash       uint32  //类型hash值;避免在哈希表中计算
    	tflag      tflag   //额外类型信息标志
    	align      uint8   //该类型变量对齐方式
    	fieldalign uint8   //该类型结构字段对齐方式   
    	kind       uint8   //类型编号
    	alg        *typeAlg //算法表 存储hash和equal两个操作。map key便使用key的_type.alg.hash(k)获取hash值
    	// gcdata stores the GC type data for the garbage collector.
    	// If the KindGCProg bit is set in kind, gcdata is a GC program.
    	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
    	gcdata    *byte   //gc数据
    	str       nameOff        // 类型名字的偏移
    	ptrToThis typeOff
    }
    



    _type 中的一些数据类型如下:

    Copy
    // typeAlg is 总是 在 reflect/type.go 中 copy或使用.
    // 并保持他们同步.
    type typeAlg struct {
    	// 算出该类型的Hash
    	// (ptr to object, seed) -> hash
    	hash func(unsafe.Pointer, uintptr) uintptr
    	// 比较该类型对象
    	// (ptr to object A, ptr to object B) -> ==?
    	equal func(unsafe.Pointer, unsafe.Pointer) bool
    }
    type nameOff int32
    type typeOff int32
    


    但是各个类型需要的类型描叙是不一样的,比如chan,除了chan本身外,还需要描述其元素类型,而map则需要key类型信息和value类型信息等:

    Copy
    //src/runtime/type.go
    
    type ptrtype struct {
    	typ  _type
    	elem *_type
    }
    
    type chantype struct {
    	typ  _type
    	elem *_type
    	dir  uintptr
    }
    
    type maptype struct {
    	typ        _type
    	key        *_type
    	elem       *_type
    	bucket     *_type // internal type representing a hash bucket
    	keysize    uint8  // size of key slot
    	valuesize  uint8  // size of value slot
    	bucketsize uint16 // size of bucket
    	flags      uint32
    }
    


    看上面的类型信息,第一个自动都是 _type,接下来也定义了一堆类型所需要的信息(如子类信息),这样在进行类型相关操作时,可通过一个字(typ *_type)即可表述所有类型,然后再通过_type.kind可解析出其具体类型,最后通过地址转换即可得到类型完整的”_type树”,参考reflect.Type.Elem()函数:

    Copy
    // reflect/type.go
    // reflect.rtype结构体定义和runtime._type一致  type.kind定义也一致(为了分包而重复定义)
    // Elem()获取rtype中的元素类型,只针对复合类型(Array, Chan, Map, Ptr, Slice)有效
    func (t *rtype) Elem() Type {
    	switch t.Kind() {
    	case Array:
    		tt := (*arrayType)(unsafe.Pointer(t))
    		return toType(tt.elem)
    	case Chan:
    		tt := (*chanType)(unsafe.Pointer(t))
    		return toType(tt.elem)
    	case Map:
    		tt := (*mapType)(unsafe.Pointer(t))
    		return toType(tt.elem)
    	case Ptr:
    		tt := (*ptrType)(unsafe.Pointer(t))
    		return toType(tt.elem)
    	case Slice:
    		tt := (*sliceType)(unsafe.Pointer(t))
    		return toType(tt.elem)
    	}
    	panic("reflect: Elem of invalid type")
    }
    

    四:没有方法的interface赋值后内部结构#


    对于没有方法的interface赋值后的内部结构是怎样的呢?
    可以先看段代码:

    Copy
    import (
    	"fmt"
    	"strconv"
    )
    
    type Binary uint64
    
    func main() {
    	b := Binary(200)
    	any := (interface{})(b)
    	fmt.Println(any)
    }
    


    输出200,赋值后的结构图是这样的:

    图片来自:https://blog.csdn.net/i6448038/article/details/82916330


    对于将不同类型转化成type万能结构的方法,是运行时的convT2E方法,在runtime包中。
    以上,是对于没有方法的接口说明。
    对于包含方法的函数,用到的是另外的一种结构,叫iface

    五:iface 非空接口#


    iface结构体表示非空接口:

    iface#

    Copy
    // runtime/runtime2.go
    // 非空接口
    type iface struct {
        tab  *itab
        data unsafe.Pointer //指向原始数据指针
    }
    

    itab#


    itab结构体是iface不同于eface,比较关键的数据结构

    Copy
    // runtime/runtime2.go
    // 非空接口的类型信息
    type itab struct {
        //inter 和 _type 确定唯一的 _type类型
        inter  *interfacetype    // 接口自身定义的类型信息,用于定位到具体interface类型
        _type  *_type        // 接口实际指向值的类型信息-实际对象类型,用于定义具体interface类型
        hash int32          //_type.hash的拷贝,用于快速查询和判断目标类型和接口中类型是一致
        _     [4]byte
        fun  [1]uintptr //动态数组,接口方法实现列表(方法集),即函数地址列表,按字典序排序
                        //如果数组中的内容为空表示 _type 没有实现 inter 接口
                        
    }
    


    属性interfacetype类似于_type,其作用就是interface的公共描述,类似的还有maptypearraytypechantype…其都是各个结构的公共描述,可以理解为一种外在的表现信息。interfacetype源码如下:

    Copy
    // runtime/type.go
    // 非空接口类型,接口定义,包路径等。
    type interfacetype struct {
       typ     _type
       pkgpath name
       mhdr    []imethod      // 接口方法声明列表,按字典序排序
    }
    
    // 接口的方法声明,一种函数声明的抽象
    // 比如:func Print() error
    type imethod struct {
       name nameOff          // 方法名
       ityp typeOff                // 描述方法参数返回值等细节
    }
    
    type nameOff int32
    type typeOff int32
    

    method 存的是func 的声明抽象,而 itab 中的 fun 字段才是存储 func 的真实切片。


    非空接口(iface)本身除了可以容纳满足其接口的对象之外,还需要保存其接口的方法,因此除了data字段,iface通过tab字段描述非空接口的细节,包括接口方法定义,接口方法实现地址,接口所指类型等。iface是非空接口的实现,而不是类型定义,iface的真正类型为interfacetype,其第一个字段仍然为描述其自身类型的_type字段。

    六:iface整体结构图#


    图片来自:https://blog.csdn.net/i6448038/article/details/82916330

    七:含有方法的interface赋值后的内部结构#


    含有方法的interface赋值后的内部结构是怎样的呢?

    Copy
    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    type Binary uint64
    func (i Binary) String() string {
    	return strconv.FormatUint(i.Get(), 10)
    }
    
    func (i Binary) Get() uint64 {
    	return uint64(i)
    }
    
    func main() {
    	b := Binary(200)
    	any := fmt.Stringer(b)
    	fmt.Println(any)
    }
    


    首先,要知道代码运行结果为:200。
    其次,了解到fmt.Stringer是一个包含String方法的接口。

    Copy
    type Stringer interface {
    	String() string
    }
    


    最后,赋值后接口Stringer的内部结构为:

    八:参考:#

    https://wudaijun.com/2018/01/go-interface-implement/
    https://blog.csdn.net/i6448038/article/details/82916330#comments

  • 相关阅读:
    PAT顶级 1024 Currency Exchange Centers (35分)(最小生成树)
    Codeforces 1282B2 K for the Price of One (Hard Version)
    1023 Have Fun with Numbers (20)
    1005 Spell It Right (20)
    1092 To Buy or Not to Buy (20)
    1118 Birds in Forest (25)
    1130 Infix Expression (25)
    1085 Perfect Sequence (25)
    1109 Group Photo (25)
    1073 Scientific Notation (20)
  • 原文地址:https://www.cnblogs.com/ExMan/p/13905698.html
Copyright © 2011-2022 走看看