GoLang之nil并不是nil
注:本文是基于Go SDK v1.8进行讲解
1.问题所在
如下的代码输出结果既不是 true也不是false。
程序抛出了异常(panic),告诉我,你在 nil 值上调用 IsNil 方法会出问题…
这可太疯狂了,他说 nil 没法调用 IsNil,那这个方法存在的意义是什么?
func main() {
x := reflect.ValueOf(nil)
fmt.Println(x) //输出:<invalid reflect.Value>
fmt.Println(x.IsNil()) //抛出如下错误
/*
panic: reflect: call of reflect.Value.IsNil on zero Value
goroutine 1 [running]:
reflect.Value.IsNil(...)
...
*/
}
func main() {
fmt.Println(nil == nil)
//输出:invalid operation: nil == nil (operator == not defined on untyped nil)
}
2.正确使用
那什么情况下会是正确显示的,设想的使用案例是:
运行结果是 true,能够正确识别。
整体情况就是:nil 不等于 nil,这与我们的传统理解相差甚远,就像 interface 的相比一样折腾开发者的直觉。
原因没有太多的探讨,但这基本是设计上的 “缺陷” 了,当时没有考虑到这一种场景,导致出现了这个问题。
type s interface{}
func main() {
var v s
elem := reflect.ValueOf(&v).Elem()
fmt.Println(elem) //输出:<nil>
fmt.Println(elem.IsNil()) //输出:true
}
3.规则
Go 核心团队的数名成员都进行了探讨,也是比较认同的。Roger Peppe 甚至给出了一个调整思路,如下:
1)reflect.ValueOf(nil) 返回 Value 的零值(就像现在这样)。
2)对于 Value 的零值,IsNil 返回 true。
3)对于 Value 的零值,Type 返回 nil。
一般来说,nil Type 代表 “无类型”,就像零 Value 代表 "无值 "一样。而 nil 是我们在 Go 中得到的最接近于代表 "无 "的通用方法,所以 IsNil 返回 true 应该是比较合理的。
4.源码注释
今天这篇文章我们讲解了除了 interface 对比外,另外一个神奇的 nil 不等于 nil 的现象。在 Go 中,这些案例其实不少,大多被发现的也在源代码上标注了注释(但不是每个人都会去翻源代码的)。
源码如下:
// IsNil reports whether its argument v is nil. The argument must be
// a chan, func, interface, map, pointer, or slice value; if it is
// not, IsNil panics. Note that IsNil is not always equivalent to a
// regular comparison with nil in Go. For example, if v was created
// by calling ValueOf with an uninitialized interface variable i,
// i==nil will be true but v.IsNil will panic as v will be the zero
// Value.
func (v Value) IsNil() bool {...}
翻译后:
//IsNil 报告其参数 v 是否为 nil;
//参数必须是chan, func, interface, map, pointer, or slice value;
//如果不是以上的参数,IsNil将painc出错误;
//请注意,IsNil 并不总是等价于在 Go 中与 nil 进行常规==比较;
//例如,如果创建了v通过使用未初始化的接口变量 i 调用 ValueOf,
//i==nil 将为真,但 v.IsNil 将panic(注:以下案例发现并没有panic而是仅仅false),因为 v 将是零值。
func (v Value) IsNil() bool {...}
type s interface{}
func main() {
var v s
fmt.Println(v == nil) //输出:true
value := reflect.ValueOf(&v)
fmt.Println(value) //输出:0xc00005a250
a := value.IsNil() //f
fmt.Println(a) //输出:false
}
参考资料:
[1]:proposal: reflect: reflect.ValueOf(nil).IsNil() panics; let’s make it return true