0%

golang的interface底层结构

在 Go 语言中,interface 和函数一样,都是“第一公民”。interface 可以用在任何使用变量的地方。可以作为结构体内的字段,可以作为函数的形参和返回值,可以作为其他 interface 定义的内嵌字段。

interface 在大型项目中常常用来解耦。在层与层之间用 interface 进行抽象和解耦。由于 Go interface 非侵入的设计,使得抽象出来的代码特别简洁,这也符合 Go 语言设计之初的哲学。

1. 分类

interface源码路径: https://github.com/golang/go/blob/master/src/runtime/runtime2.go

interface在使用的过程中,共有两种表现形式,一种为空接口(empty interface),另一种为非空接口(non-empty interface)。

两种interface类型分别用两种struct表示,空接口为eface数据结构,非空接口为iface数据结构

qq730260-0

1.1 空接口 eface

空接口eface结构,由两个属性构成,一个是类型信息_type,另一个是数据信息,其数据结构声明如下:

qq730260-1
1. _type类型

此属性是Go语言中所有类型的公共描述,Go语言绝大多数的数据结构可以抽象成_type属性,是所有类型的公共描述,type负责决定data应该如何解释和操作,type的结构代码如下:

qq730260-2
2. data属性

表示指向具体的实例数据的指针,它是一个unsafe.Pointer类型,相当于C语言的一个万能指针void∗。

1.2 非空接口 iface

1
2
3
4
5
6
7
8
9
10
11
12
type iface struct {
tab *itab
data unsafe.Pointer
}

type itab struct {
inter *interfacetype //接口自身的元信息
_type *_type //具体类型的元信息
hash uint32 //_type里也有一个同样的hash,此处多放一个是为了方便运行接口断言
_ [4]byte
fun [1]uintptr //函数指针,指向具体类型所实现的方法
}
  1. interfacetype包含了一些关于interface本身的信息,例如package path,包含了method。这里的interfacetype是定义interface的一种抽象表示。
  2. type表示具体化的类型,与eface的type类型相同。
  3. hash字段其实是对_type.hash的复制,它会在interface的实例化时,用于快速判断目标类型和接口中的类型是否一致。Go语言的interface的Duck-typing机制也依赖这个字段实现。
  4. fun字段其实是一个动态大小的数组,虽然声明时固定大小为1,但在使用时会直接通过fun指针获取其中的数据,并且不会检查数组的边界,所以该数组中保存的元素数量是不确定的。
1. 举例
1
2
3
4
5
6
7
8
type People interface{
Show()
}

func live() People {
var stu *Student
return stu
}

live( )返给上层的是一个People interface{}类型,也就是一个iface struct{}类型。stu为nil,只是iface中的data为nil而已,但是iface struct{}本身并不为nil

Figure-P234_6753

2. Type Assertion 断言

我们知道使用 interface 断言的时候需要注意,不然很容易引入 panic。

2.1 断言

1
2
3
4
5
6
7
8
9
10
func do(v interface{}) {
n := v.(int) // might panic
}

func do(v interface{}) {
n, ok := v.(int)
if !ok {
// 断言失败处理
}
}

这个过程体现在下面的几个函数上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// The assertXXX functions may fail (either panicking or returning false,
// depending on whether they are 1-result or 2-result).
func assertI2I(inter *interfacetype, i iface) (r iface) {
tab := i.tab
if tab == nil {
// explicit conversions require non-nil interface value.
panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
}
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 assertI2I2(inter *interfacetype, i iface) (r iface, b bool) {
tab := i.tab
if tab == nil {
return
}
if tab.inter != inter {
tab = getitab(inter, tab._type, true)
if tab == nil {
return
}
}
r.tab = tab
r.data = i.data
b = true
return
}

// 类似
func assertE2I(inter *interfacetype, e eface) (r iface)
func assertE2I2(inter *interfacetype, e eface) (r iface, b bool)

3. 头脑风暴

  • interface 分为两种,空接口 eface 和 非空接口 iface。
  • 参数interface之所以不等于nil,是因为内部的data指向了nil,但是data不等于nil。
  • 两个底层实现都有一个 _type 结构,进行断言。

4. 参考资料

给作者打赏,可以加首页微信,咨询作者相关问题!