引言
- 从runtime包分析学习interface底层结构和interface断言的实现、
是nil,但不完全是nil
1 | func InterfaceTest(v interface{}) { |
运行main方法,查看输出结果发现:InterfaceTest2是符合预期的。从InterfaceTest的输出来看,v是nil,但不完全为nil,经过interface转换之后发生了什么?
1 | <nil> |
interface底层结构
interface判断与预期不一样的根本原因,interface根本不是那么简简单单,而是两个struct。什么?interface不是单纯的值?是struct,还是两个?
根据interface是否包含method,底层实现上有两种struct:eface,iface
eface表示不包含任何方法的空接口,也称为 empty interface。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18type 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变量里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24type 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
}
类型断言是怎么实现的?
断言分为安全和非安全两种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18func 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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19func 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)