什么?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。