对于下面这段代码,我们知道 i
实际上的值就是 nil
,所以 i == nil
会生效
func main() {
var i *int = nil
if i == nil {
fmt.Println("i is nil") // i is nil
}
}
现在换一种写法,我们将 i
的类型改成 interface{}
,i == nil
依然会生效
func main() {
var i interface{} = nil
if i == nil {
fmt.Println("i is nil") // i is nil
}
}
我们接着改造,将 i == nil
的逻辑封装成函数 IsNil
func IsNil(i interface{}) {
if i == nil {
fmt.Println("i is nil")
}
}
func main() {
var i *int = nil
IsNil(i)
}
然后居然发现 IsNil
中的 i == nil
不生效了,为什么呢?
因为对于 interface{}
类型的值来说,如果要判断它是 nil
,必须同时满足 type T
和 value V
都是 nil
才行
可以用 reflect
中的 TypeOf
和 ValueOf
var i *int = nil
fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i)) // *int <nil>
var i interface{} = nil
fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i)) // <nil> <invalid reflect.Value>
但是如果我们在函数中用 interface{}
作为参数的类型,表示并不代表参数就是 interface{}
类型,而是任意类型,调用时传入啥类型就是啥类型,如下代码
var i interface{} = 1
fmt.Println(reflect.TypeOf(i)) // int
var j interface{} = "hello"
fmt.Println(reflect.TypeOf(j)) // string
var k interface{} = nil
fmt.Println(reflect.TypeOf(k)) // nil
所以只有当我们传入类型的参数是 interface{}
类型时,且 value
为 nil
时,i == nil
才会生效
否则其他情况都不会生效
func main() {
var i interface{} = nil
IsNil(i) // i is nil
}
func IsNil(i interface{}) {
if i == nil {
fmt.Println("i is nil")
}
}
这个坑可能会出现在返回 error
的函数中,比如下面这段代码
在函数 SomeThing
中提前定义了 myError
,然后一系列的处理后,返回了 myError
后面的业务逻辑需要判断 err
是否为 nil
type MyError struct{}
func (me *MyError) Error() string {
return "my error"
}
func SomeThing() error {
var myError *MyError // 默认初始化为 nil
// ...
return myError
}
func main() {
err := SomeThing()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // *main.MyError <nil>
if err != nil { // 虽然没有返回,这里会被执行,因为 err 的类型不是 nil
fmt.Println(err)
}
}
从上面的代码我们看到,SomeThing
函数中定义的 myError
是 *MyError
类型,虽然返回了 nil
,但是 err
的类型不是 nil
,所以 err != nil
会生效,不符合预期
如果修改这个问题呢,当我们需要返回 nil
时,显示指明返回 nil
,如下代码:
type MyError struct{}
func (me *MyError) Error() string {
return "my error"
}
func SomeThing() error {
var myError *MyError // 默认初始化为 nil
// ...
return nil
}
func main() {
err := SomeThing()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // <nil> <invalid reflect.Value>
if err != nil { // 这段代码不会被执行
fmt.Println(err)
}
}
总结:需要返回 nil
时,要显示返回 nil
,不要用指针类型的零值
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。