• 浅析 golang interface 实现原理


    interface 在 golang 中是一个非常重要的特性。它相对于其它语言有很多优势:

    1. duck typing。大多数的静态语言需要显示的声明类型的继承关系。而 golang 通过 interface 实现了 duck typing, 使得我们无需显示的类型继承。
    2. 不像其它实现了 duck typing 的动态语言那样,只能在运行时才能检查到类型的转换错误。而 golang 的 interface 特性可以让我们在编译时就能发现错误。

    本文将简单分析 interface 的实现原理。


    interface 的数据结构

    eface 和 iface

    image

    • eface 表示空的 interface{},它用两个机器字长表示,第一个字 _type 是指向实际类型描述的指针,第二个字 data 代表数据指针。
    • iface 表示至少带有一个函数的 interface, 它也用两个机器字长表示,第一个字 tab 指向一个 itab 结构,第二个字 data 代表数据指针。

    data

    data 用来保存实际变量的地址。

    data 中的内容会根据实际情况变化,因为 golang 在函数传参和赋值时是 值传递 的,所以:

    1. 如果实际类型是一个值,那么 interface 会保存这个值的一份拷贝。interface 会在堆上为这个值分配一块内存,然后 data 指向它。
    2. 如果实际类型是一个指针,那么 interface 会保存这个指针的一份拷贝。由于 data 的长度恰好能保存这个指针的内容,所以 data 中存储的就是指针的值。它和实际数据指向的是同一个变量。

    以 interface{} 的赋值为例:

    image

    上图中, i1 和 i2 是 interface,A 为要赋值给 interface 的对象。

    • i1 = A 将 A 的值赋值给 i1,则 i1 中的 data 中的内容是一块新内存的地址 (0x123456),这块内存的值从 A 拷贝。
    • i2 = &A 将 A 的地址赋值给 i2,则 i2 中的 data 的值为 A 的地址,即 0xabcdef;

    itab

    image

    itab 表示 interface 和 实际类型的转换信息。对于每个 interface 和实际类型,只要在代码中存在引用关系, go 就会在运行时为这一对具体的 <Interface, Type> 生成 itab 信息。

    • inter 指向对应的 interface 的类型信息。
    • type 和 eface 中的一样,指向的是实际类型的描述信息 _type
    • fun 为函数列表,表示对于该特定的实际类型而言,interface 中所有函数的地址。

    _type

    image

    _type 表示类型信息。每个类型的 _type 信息由编译器在编译时生成。其中:

    • size 为该类型所占用的字节数量。
    • kind 表示类型的种类,如 bool、int、float、string、struct、interface 等。
    • str 表示类型的名字信息,它是一个 nameOff(int32) 类型,通过这个 nameOff,可以找到类型的名字字符串
    • 灰色的 extras 对于基础类型(如 bool,int, float 等)是 size 为 0 的,它为复杂的类型提供了一些额外信息。例如为 struct 类型提供 structtype,为 slice 类型提供 slicetype 等信息。
    • 灰色的 ucom 对于基础类型也是 size 为 0 的,但是对于 type Binary int 这种定义或者是其它复杂类型来说,ucom 用来存储类型的函数列表等信息。
    • 注意 extras 和 ucom 的圆头箭头,它表示 extras 和 ucom 不是指针,它们的内容位于 _type 的内存空间中。

    interfacetype

    image

    interfacetype 也并没有什么神奇的地方,只是 _type 为 interface 类型提供的另一种信息罢了。 它包括这个 interface 所申明的所有函数信息。


    interface 相关的操作

    itab 中函数表(fun) 的生成

    假设 interface 有 ni 个函数, struct 有 nt 个函数,那么 itab 中的函数表生成的时间复杂度为 O(ni*nt) (遍历 interface 的所有函数,对每次迭代都从 struct 中遍历找到匹配的函数)。 但实际上编译器对此做了优化,它将 interfacetype 中的函数列表和 uncommontype 中的函数列表都做了排序. 所以实现了 O(ni+nt) 时间复杂度的算法。

    // 生成 itab 的 funcs 的算法
    // 代码摘录自 $GOROOT/src/runtime/iface.go
    // 经过了部分修改,只保留了最核心的逻辑
    
    var j = 0
    for k := 0; k < ni; k++ {
        mi := inter.methods[k]
        for ; j < nt; j++ {
            mt := t.methods[j]
            if isOk(mi, mt) {
                itab.fun[k] = mt.f
            }
        }
    }
    

    interface 参数传递与函数调用

    type Binary uint64
    
    func (i Binary) String() string {
        return strconv.FormatUint(uint64(i), 10)
    }
    
    type Stringer interface {
        String() string
    }
    
    func test(s Stringer) {
        s.String()
    }
    
    func main() {
        b := Binary(0x123)
        test(b)
    }
    

    在上面的代码中,golang 的参数传递过程是:

    1. 分配一块内存 p, 并且将对象 b 的内容拷贝到 p 中;
    2. 创建 iface 对象 i,将 i.tab 赋值为 itab<Stringer, Binary>。将 i.data 赋值为 p;
    3. 使用 i 作为参数调用 test 函数。

    当 test 函数执行 s.String 时,实际上就是在 s.tab 的 fun 中索引(索引由编译器在编译时生成)到 String 函数,并且调用它。


    参考资料:

    1. Why I like Go’s interfaces
    2. duck typing
    3. Go Data Structures: Interfaces
    4. go 源码 https://github.com/golang/go/tree/master/src/runtime

    本文始发于https://zhuanlan.zhihu.com/p/60983066

  • 相关阅读:
    Elementary Methods in Number Theory Exercise 1.3.13
    Elementary Methods in Number Theory Exercise 1.3.17, 1.3.18, 1.3.19, 1.3.20, 1.3.21
    数论概论(Joseph H.Silverman) 习题 5.3,Elementary methods in number theory exercise 1.3.23
    Elementary Methods in Number Theory Exercise 1.2.31
    数论概论(Joseph H.Silverman) 习题 5.3,Elementary methods in number theory exercise 1.3.23
    Elementary Methods in Number Theory Exercise 1.3.13
    Elementary Methods in Number Theory Exercise 1.3.17, 1.3.18, 1.3.19, 1.3.20, 1.3.21
    Elementary Methods in Number Theory Exercise 1.2.31
    Elementary Methods in Number Theory Exercise 1.2.26 The Heisenberg group
    4__面向对象的PHP之作用域
  • 原文地址:https://www.cnblogs.com/anjiawei/p/10664978.html
Copyright © 2020-2023  润新知