interface
底层结构
#
interface{}
底层存储结构如下所示:
// ----------- runtime/runtime2.go -----------------
type iface struct {
tab *itab
data unsafe.Pointer
}
type eface struct {
_type *_type
data unsafe.Pointer
}
type itab = abi.ITab
type _type = abi.Type
// ----------- internal/abi/iface.go -----------------
// The first word of every non-empty interface type contains an *ITab.
// It records the underlying concrete type (Type), the interface type it
// is implementing (Inter), and some ancillary information.
//
// allocated in non-garbage-collected memory
type ITab struct {
Inter *InterfaceType
Type *Type
Hash uint32 // copy of Type.Hash. Used for type switches.
Fun [1]uintptr // variable sized. fun[0]==0 means Type does not implement Inter.
}
可以看到 interface{}
底层被存储为两种类型:
iface
表示有方法集的接口类型变量eface
表示没有方法的空接口类型变量,也就是interface{}
类型的变量
interface
注意要点
#
判断 interface{} 是否等于 nil #
比如以下函数 returnError
返回 error
, 处理返回的 error
时要判断是不是 nil
type MyError string
func (e *MyError) Error() string { return string(*e) }
func bad() bool { return false }
func returnError() error {
var err *MyError = nil
if bad() {
badErr := MyError("bad error")
err = &badErr
}
return err
}
既然是注意点,那就肯定不是简单的 v == nil
的方式比较。
首先简化一下上述函数,因为一直不会进入
if bad() {}
, 所以简化为
func returnError() error {
var err *MyError = nil
return err
}
这里也要注意,上述代码和和**直接
var err *MyError = nil
然后比较err
是否为nil
是不一样的。**这种err
是*MyError
类型,可以直接与nil
比较并且相等
,不是interface{}
而 func returnError() error
相当与如下代码,返回的是 interface{}
:
var ret error
var err *MyError = nil
ret = err
return ret
上述代码中, 相当于把一个类型为
*MyError
但是值为nil
的变量赋值给了inerface{}
类型的ret
,因为ret
有实现error
接口的方法func Error() string
,所以底层使用iface
存储,其中:
iface.tab.Type
会存储*MyError
类型iface.data
为 nil当
ret
直接与nil
比较时会先比较类型,*MyError
和nil
不相等,如果直接比较就容易被坑!!!
所以:
当调用一些奇怪函数返回 interface{}
类型变量或者函数接收 interface{}
类型变量并且需要判断是否为 nil
时就要张个心眼。可以使用如下函数判断一个 interface{}
是否为 nil
:
func IsNil(v interface{}) bool {
if v == nil {
return true
}
switch reflect.TypeOf(v).Kind() {
case reflect.Chan, reflect.Func, reflect.Slice,
reflect.Map, reflect.Ptr:
return reflect.ValueOf(v).IsNil()
default:
return false
}
}