Golang中interface的nil判断
背景
最近在使用Golang的过程中遇到了一些有意思的问题,主要是关于interface
的nil
判断。
示例代码
1 | package main |
运行结果
以上代码是基本还原了我的遇到的情况,运行后会panic
1 | panic: runtime error: invalid memory address or nil pointer dereference |
我们尝试将main
函数修改成如下:
1 | func main() { |
正常运行,输出如下:
1 | input a nil data |
原因分析
开始的时候,虽然我们显式的将指针赋值为nil,但是显然没有生效。我们对结果进行打印
1 | fmt.Println(fmt.Sprintf("data:%v, data is nil:%v", shape, shape == nil)) |
输出结果如下:
1 | data:<nil>, data is nil: false |
还是看不出问题所在。
在网上查询相关资料,找到了官方的一些相关解释 nil_error:
Under the covers, interfaces are implemented as two elements, a type T and a value V. V is a concrete value such as an int, struct or pointer, never an interface itself, and has type T. For instance, if we store the int value 3 in an interface, the resulting interface value has, schematically, (T=int, V=3). The value V is also known as the interface’s dynamic value, since a given interface variable might hold different values V (and corresponding types T) during the execution of the program.
An interface value is nil only if the V and T are both unset, (T=nil, V is not set), In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (T=*int, V=nil). Such an interface value will therefore be non-nil even when the pointer value V inside is nil.
简单来说就是interface底层实现是由两个元素组成: Type 和 Value。 只有两个都没有被设置的时候,interface才是nil。
综上,之所以我们将circle显式的置为nil,但是仍然失败,就是因为circle的type已经被设置过了。我们添加打印来印证我们的猜测:
1 | fmt.Println(fmt.Sprintf("data:%v, type:%T, data is nil: %v", shape, shape, shape == nil)) |
结果如下,印证了上边的说法:
1 | data:<nil>, type:*main.Circle data is nil: false |
然后我尝试将打印语句从test函数移入到main中,得到了截然不同的结果
1 | data:<nil>, type:*main.Circle, data is nil: true |
原因是什么呢?很简单,这里的circle是pointer,而不是interface。