定义
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,导致最终判断结果不符合预期。