2.4 nil的语义

什么?nil是一种数据结构么?为什么会讲到它,没搞错吧?没搞错。不仅仅是Go语言中,每门语言中nil都是非常重要的,它代表的是空值的语义。

在不同语言中,表示空这个概念都有细微不同。比如在scheme语言(一种lisp方言)中,nil是true的!而在ruby语言中,一切都是对象,连nil也是一个对象!在C中NULL跟0是等价的。

按照Go语言规范,任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是””,而指针,函数,interface,slice,channel和map的零值都是nil。

interface

一个interface在没有进行初始化时,对应的值是nil。也就是说var v interface{}

此时v就是一个nil。在底层存储上,它是一个空指针。与之不同的情况是,interface值为空。比如:

  1. var v *T
  2. var i interface{}
  3. i = v

此时i是一个interface,它的值是nil,但它自身不为nil。

Go中的error其实就是一个实现了Error方法的接口:

  1. type error interface {
  2. Error() string
  3. }

因此,我们可以自定义一个error:

  1. type Error struct {
  2. errCode uint8
  3. }
  4. func (e *Error) Error() string {
  5. switch e.errCode {
  6. case 1:
  7. return "file not found"
  8. case 2:
  9. return "time out"
  10. case 3:
  11. return "permission denied"
  12. default:
  13. return "unknown error"
  14. }
  15. }

如果我们这样使用它:

  1. func checkError(err error) {
  2. if err != nil {
  3. panic(err)
  4. }
  5. }
  6. var e *Error
  7. checkError(e)

e是nil的,但是当我们checkError时就会panic。请读者思考一下为什么?

总之,interface跟C语言的指针一样非常灵活,关于空的语义,也跟空指针一样容易困扰新手的,需要注意。

string和slice

string的空值是””,它是不能跟nil比较的。即使是空的string,它的大小也是两个机器字长的。slice也类似,它的空值并不是一个空指针,而是结构体中的指针域为空,空的slice的大小也是三个机器字长的。

channel和map

channel跟string或slice有些不同,它在栈上只是一个指针,实际的数据都是由指针所指向的堆上面。

跟channel相关的操作有:初始化/读/写/关闭。channel未初始化值就是nil,未初始化的channel是不能使用的。下面是一些操作规则:

  • 读或者写一个nil的channel的操作会永远阻塞。
  • 读一个关闭的channel会立刻返回一个channel元素类型的零值。
  • 写一个关闭的channel会导致panic。

map也是指针,实际数据在堆中,未初始化的值是nil。