当将一个值为nil的接口变量w赋值给空接口类型i后,i为nil。
// case 1
var i interface{}
var w io.Writer
i = w
print(i == nil) // true
但是将一个值为nil的接口指针变量w赋值给空接口类型i后,i就不等于nil了。
// case 2
var i interface{}
var w *io.Writer
i = w
print(i == nil) // false
what???
当我无意间测试到这种case之后,缺失有点不知所以。
下面我们就通过反射和debug查看到底发生了什么。
有个常识需要再提一下:判断一个空接口类型的变量是否为nil时,只有当type和value同时为nil时,空接口类型变量才为nil。
有个进阶的知识需要提前说一下:空接口类型数据在类型系统中的表示对应的是runtime.eface,如下所示;在反射包中有对应结构,那就是:reflect.emptyInterface,结构如下:
type eface struct {
_type *_type // 类型信息
data unsafe.Pointer // 值信息
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype // 类型信息
word unsafe.Pointer // 值信息
}
先用goland debug一下(使用reflect.TypeOf方法测试i的类型信息)。
case 1的情况:
case 2的情况:
可以看到case1的type为nil,而case2的type不为nil,所以case 2的i不为nil。
下面通过调试工具delve来看一下结果:
case 1:
case 2:
和显然和goland中调试结果是一致的。只不过我们在使用delve调试的时候,可以输出空接口runtime的表示,而goland ide只能通过调用反射功能窥视类型数据。
另外,我们注意到case 2中kind=54,和我们见到的reflect.Kind枚举数据对不上。则是因为kind: 54需要和kindMask = (1 << 5) - 1,做一次与运算就和reflect.Kind对应了,与运算后是22,就是reflect.Ptr的值。
func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)