摘要
Go
中的nil
只能用来表示指针、接口、通道、映射、切片和函数类型的零值,非以上6种类型的是不能使用nil
表示的。
正文
nil
的定义
Go
中的nil
与其它语言中的null
是一样的吗?
有Java
或其他编程语言背景的话,你可能认为它们是一样的,但其实它们部分相同但又有些不同。
nil
是 Go
中预先声明的标识符,表示指针、接口、通道、映射、切片和函数类型的零值。 nil
是指针和接口的零值,未初始化的指针和接口将为nil
。这是否意味着字符串不能为nil
? 是的! 字符串不符合上述任何一种类型因此不能被赋值为 nil
给不指定类型的变量赋值nil
那我用nil
做一个短声明会怎样呢?
a := nil
这将触发一个编译时错误use of untyped nil
,因为 nil 是非类型化(untyped
)的,并且是唯一没有默认类型的非类型化值,编译器不知道它必须分配给a
的类型。
接下来,让我们尝试为限定类型(指针)赋值
var *a int = nil
上述代码会按我们期望的那样成功运行。
函数返回nil
如果我尝试从函数返回 nil
会怎样?
package main
import "fmt"
type A struct {
str string
}
func main() {
s := getA()
fmt.Println(s.str) // This will crash
}
func getA() *A {
return nil // This WILL compile
}
上面的程序会因为这个错误而恐慌(panic
)。
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1092534]
但它不是一个编译时错误,因为我们返回了一个 nil
指针,指针正是 nil
的限定类型.
两个nil
值能互相比较吗
让我们看看这两个代码片段
var m []int = nil
var n []int = nil
if m == n {
fmt.Println("equal")
}
这将会产生一个编译时错误:
invalid operation: m == n (slice can only be compared to nil)
我们再看下一个
var i someIntf = nil
var j someIntf = nil
if i == j{
fmt.Println("equal")
}
上面代码可以工作,并且打印出equal
为什么会这样?
因为在 Go
中,map
、slice
和 function
类型不支持比较。不可比较类型的两个nil
值是不允许比较的。
但是不可比较的类型可以与裸 nil
标识符进行比较。
var m []int = nil
var n []int = nil
if m == nil && n == nil {
fmt.Println("both are nil")
}