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 。

1

https://golang.org/ref/spec#The_zero_value

2

https://github.com/golang/go/blob/release-branch.go1.9/src/runtime/print.go#L231

3

https://golang.org/doc/faq#nil_error


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 就区分开来了。