Go 语言实现——nil¶
按照 Go 语言规范,任何类型在未初始化时都对应一个零值,boolean 是 false,int 是 0,而 pointer, function, interface, slice, channel, map 的零值是 nil 1 。
我们用下面这段简单的程序来看下各种类型的 nil 到底是什么。
package main
func main() {
var p *int
println(p)
var f func()
println(f)
var i interface{}
println(i)
var s []int
println(s)
var m map[int]int
println(m)
var c chan int
println(c)
}
运行的输出结果(// 后是注释):
0x0 // pointer
0x0 // function
(0x0,0x0) // interface
[0/0]0x0 // slice
0x0 // map
0x0 // channel
反编译可以看到 print 语句被翻译为了以下调用:
test.go:9 0x782 e800000000 CALL 0x787 [1:5]R_CALL:runtime.printpointer
...
test.go:12 0x7a8 e800000000 CALL 0x7ad [1:5]R_CALL:runtime.printpointer
...
test.go:15 0x7e1 e800000000 CALL 0x7e6 [1:5]R_CALL:runtime.printeface
...
test.go:18 0x82d e800000000 CALL 0x832 [1:5]R_CALL:runtime.printslice
...
test.go:21 0x853 e800000000 CALL 0x858 [1:5]R_CALL:runtime.printpointer
...
test.go:24 0x879 e800000000 CALL 0x87e [1:5]R_CALL:runtime.printpointer
打印的实现代码在 src/runtime/print.go 中 2 。
综上可以得出:
pointer, function, map, channel 的零值都是空指针(0)。
slice, interface 是各自的结构体中所有的值都是 0 。
最后,我们来看看 Go 官方文档 FAQ 里提到的一个问题 3 :
type MyError struct{}
func (e *MyError) Error() string {
return ""
}
func returnsError() error {
var p *MyError = nil
return p
}
func main() {
println(returnsError() == nil)
}
这段代码的输出为 false ,因为 returnsError 的返回值类型 error 是一个 interface ,所以它返回的是 (*MyError, nil) ,也就是说返回的 interface 的值是 nil,但是其本身并不是 nil 。
Go 是如何区分 empty slice 和 nil 的?
我们可以使用 reflect 包来看下 slice 的底层数据结构中的内容:
var s1 []int
s2 := []int{}
s3 := make([]int, 0)
fmt.Printf("s1 (addr: %p): %+8v\n",
&s1, *(*reflect.SliceHeader)(unsafe.Pointer(&s1)))
fmt.Printf("s2 (addr: %p): %+8v\n",
&s2, *(*reflect.SliceHeader)(unsafe.Pointer(&s2)))
fmt.Printf("s3 (addr: %p): %+8v\n",
&s3, *(*reflect.SliceHeader)(unsafe.Pointer(&s3)))
以上代码中 s1 是 nil,s2,s3 是 empty slice,下面是程序运行的结果:
s1 (addr: 0xc0000ae040): {Data: 0 Len: 0 Cap: 0}
s2 (addr: 0xc0000ae060): {Data: 5788776 Len: 0 Cap: 0}
s3 (addr: 0xc0000ae080): {Data: 5788776 Len: 0 Cap: 0}
可以看到 nil 的结构体中是所有的值都为 0,但 empty slice 中指向底层数组的指针并不指向 0,因为数组的长度为 0,所以这个指针指向哪儿其实都可以,这样 nil 和 empty slice 就区分开来了。