小提示, 若想直接查看原理, 可从接口原理开始查看.
GO
func main() {
var obj interface{}
fmt.Printf("obj == nil. %b\n", obj == nil)
type st struct{}
var s *st
obj = s
fmt.Printf("s == nil. %b\n", s == nil)
fmt.Printf("obj == nil. %b\n", obj == nil)
}
先盲猜一下结果.
niltruestrueobjstrue
如果你也是这么认为, 那么结果会令你像我一样十分惊讶:
objnil
搭建 gdb 调试环境
gdb
gdb
dockergolang:1.18
# 安装 gdb 工具
apt update && apt install -y gdb
echo 'add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py' > /root/.gdbinit
# 编译 go 文件. 关闭所有的优化, 防止调试时与编写的内容不一致
go build -gcflags "all=-N -l" main.go
# 进行调试
gdb ./main
是不是很简单呀.
调试与揭秘为了方便调试, 我将无关内容去掉, 调试使用的程序如下:
package main
func main() {
var obj interface{}
type st struct{}
var s *st
println(obj == nil)
obj = s
println(obj == nil)
}
obj
objobj == nilTRUEobj
objobj == nilFALSE_type
在此处, 我有理由得出这样的结论:
golanginterface_type/datainterfacenilnilinterfacenilnil
obj
interfaceruntime.eface
internfaceinterface_type_typenilinterfacenil
以上, 是我本次问题查找的原因及初步查找的过程. 我基于此对接口的实现原理进行了查阅. 后面就直接进行原理介绍, 不再穿插查找过程了, 否则着实影响观看体验.
接口原理GO
runtime.eface
type TestInter interface {
}
var obj interface{}
var obj2 TestInter
runtime.iface
type TestInter interface {
testFunc()
}
var obj2 TestInter
eface
efaceruntime2.go
type eface struct {
_type *_type // 保存类型信息
data unsafe.Pointer // 保存内容
}
type _type struct {
size uintptr // 类型大小
ptrdata uintptr // 没整明白是干什么用的...
hash uint32 // 类型的哈希值. 可用于快速判断类型是否相等
tflag tflag // 类型的额外信息
align uint8 // 变量的内存对齐大小
fieldAlign uint8
kind uint8 // 类型
equal func(unsafe.Pointer, unsafe.Pointer) bool // 比较此类型对象是否相等
gcdata *byte // 垃圾收集的 GC 数据
str nameOff
ptrToThis typeOff
}
// Pointer 就是一个指针
type Pointer *ArbitraryType
type ArbitraryType int
_type
_type
iface
ifaceefaceiface
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype // 保存接口的信息. 用于确定变量的接口类型
_type *_type // data 指向值得类型信息, 上面已经出现过了
hash uint32 // 从 _type.hash 拿过来的. 当将 interface 类型变量向下转型时, 用于快速判断.
_ [4]byte
// 记录接口实现的所有方法.
// 若 fun[0]==0, 说明 _type 没有实现此接口.
// (没错, 是有可能没实现的. 比如转型失败)
// 否则, 说明实现了此接口. 所有方法的函数指针在内存中顺序存放.
// fun[0] 记录的是第一个方法的地址
// 顺便提一句, 函数按照名称的字段序在内存中存放
fun [1]uintptr
}
type interfacetype struct {
typ _type // 接口类型
pkgpath name // 包名
mhdr []imethod // 接口定义的方法集
}
interfaceGO
interfaceGOruntime.iface.goconvunsafe.Pointer
总结
GOinterfacenilnil
GO1.18interface