定义
nil
不是golang的关键词,只是一个变量名。定义在buildin/buildin.go
中
// nil 是一个预先声明的标识符,表示指针、通道、函数、接口、映射或切片类型的零值。
var nil Type // 类型必须是指针、通道、函数、接口、映射或切片类型
// Type在这里仅用于文档目的。它是任何 Go 类型的替代品,但代表任何给定函数调用的相同类型。
type Type int
复制代码
我们甚至可以自己声明一个名为nil
的变量来将其覆盖掉,当然,这是不推荐的。
从定义的注释中可以看出,nil
用来表示指针、通道、函数、接口、映射或切片类型的零值。
零值
在golang声明一个变量的时候,我们如果没有对其进行显式初始化,那么该变量将会被赋予一个零值。
数据类型 | 零值 |
---|---|
布尔类型 | false |
数值类型 | 0 |
字符串 | "" |
引用类型 | nil |
结构体和数组会递归初始化其字段或者元素,其初始值取决于元素或字段。
Panic
给空指针赋值
var i *int
*i = 1 // panic
复制代码
解决办法
给空指针分配一块内存即可
var i *int
i = new(int)
*i = 1 // panic
复制代码
从空指针取值
var i *int
a := *i // panic
复制代码
解决办法
赋值前,判断该指针是否为空
var i *int
var a int
if i != nil {
a = *i
}
// a = 0
复制代码
接口类型
golang中有一个很出名的nil != nil
的例子:
var x *int
var y interface{}
fmt.Println(x == nil) // true
fmt.Println(y == nil) // true
fmt.Println(x == y) // false
复制代码
这个结果跟interface
的实现原理有关,这里不详细展开解释,简单来说,一个interface
需要通过type
和value
两个属性来确定值。
给上面的代码加上注释,就清晰了:
var x *int // type = *int, value = nil
var y interface{} // type = nil, value = nil
fmt.Println(x == y) // (*int, nil) == (nil, nil) => false
复制代码
错误处理的坑
type MyError struct {}
func (m MyError) Error() string { return "my error" }
func test() error {
var err *MyError
return err
}
func main() {
err := test()
fmt.Println(err) // <nil>
fmt.Println(err == nil) // false
}
复制代码
我们会发现,直接输出err的值时,的确是<nil>
。将其与nil
相比较时,得到的结果又是false
。
有了上面的例子,我们就知道,原来是在test()
中改变了err
的type
,导致最终判断结果不符合预期。