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值为空。比如:
var v *T
var i interface{}
i = v
此时i是一个interface,它的值是nil,但它自身不为nil。
Go中的error其实就是一个实现了Error方法的接口:
type error interface {
Error() string
}
因此,我们可以自定义一个error:
type Error struct {
errCode uint8
}
func (e *Error) Error() string {
switch e.errCode {
case 1:
return "file not found"
case 2:
return "time out"
case 3:
return "permission denied"
default:
return "unknown error"
}
}
如果我们这样使用它:
func checkError(err error) {
if err != nil {
panic(err)
}
}
var e *Error
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。