Golang进阶

意料之外:空接口和nil的比较

2021-12-11  本文已影响0人  qishuai

当将一个值为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的情况:


image.png

case 2的情况:


image.png

可以看到case1的type为nil,而case2的type不为nil,所以case 2的i不为nil。

下面通过调试工具delve来看一下结果:
case 1:


image.png

case 2:


image.png

和显然和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
)
上一篇 下一篇

猜你喜欢

热点阅读