golang的nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。nil是預先說明的標識符,也即通常意義上的關鍵字。在golang中,nil只能賦值給指針、channel、func、interface、map或slice類型的變量。如果未遵循這個規則,則會引發panic。對此官方有明確的說明:http://pkg.golang.org/pkg/builtin/#Type
golang中的interface類似于java的interface、php的interface或C++的純虛基類。接口就是一個協議,規定了一組成員。這個沒什么好說的,本文不打算對宏觀上的接口概念和基于接口的范式編程做剖析。golang語言的接口有其獨到之處:只要類型T的公開方法完全滿足接口I的要求,就可以把類型T的對象用在需要接口I的地方。這種做法的學名叫做Structural Typing,有人也把它看作是一種靜態的Duck Typing。所謂類型T的公開方法完全滿足接口I的要求,也即是類型T實現了接口I所規定的一組成員。
在底層,interface作為兩個成員來實現,一個類型和一個值。對此官方也有文檔說明:http://golang.org/doc/go_faq.html#nil_error,如果您不習慣看英文,這里有一篇柴大的翻譯:Go中error類型的nil值和nil 。
接下來通過編寫測試代碼和gdb來看看interface倒底是什么。會用到反射,如果您不太了解golang的反射是什么,這里有刑星翻譯自官方博客的一篇文章:反射的規則,原文在:laws-of-reflection。
[cpp] view plain copy接下來說說interface類型的值和nil的比較問題。這是個比較經典的問題,也算是golang的一個坑。
接下來看看代碼
[cpp] view plain copy對于將任何其它有意義的值類型賦值給val,都導致val持有一個有效的類型和數據。也就是說變量val的底層結構肯定不為(nil,%20nil),因此它和nil的相等比較總是為false。
上面的討論都是在圍繞值類型來進行的。在繼續討論之前,讓我們來看一種特例:(*interface{})(nil)。將nil轉成interface類型的指針,其實得到的結果僅僅是空接口類型指針并且它指向無效的地址。注意是空接口類型指針而不是空指針,這兩者的區別蠻大的,學過C的童鞋都知道空指針是什么概念。
關于(*interface{})(nil)還有一些要注意的地方。這里僅僅是拿(*interface{})(nil)來舉例,對于(*int)(nil)、(*byte)(nil)等等來說是一樣的。上面的代碼定義了接口指針類型變量val,它指向無效的地址(0x0),因此val持有無效的數據。但它是有類型的(*interface{})。所以val的底層結構應該是:(*interface{},%20nil)。有時候您會看到(*interface{})(nil)的應用,比如var%20ptrIface%20=%20(*interface{})(nil),如果您接下來將ptrIface指向其它類型的指針,將通不過編譯。或者您這樣賦值:*ptrIface%20=%20123,那樣的話編譯是通過了,但在運行時還是會panic的,這是因為ptrIface指向的是無效的內存地址。其實聲明類似ptrIface這樣的變量,是因為使用者只是關心指針的類型,而忽略它存儲的值是什么。還是以例子來說明:
[cpp] view%20plain copy我們可以來分析一下。error是一個接口類型,test方法中返回的指針p雖然數據是nil,但是由于它被返回成包裝的error類型,也即它是有類型的。所以它的底層結構應該是(*data,%20nil),很明顯它是非nil的。可以打印觀察下底層結構數據:
[cpp] view%20plain copy新聞熱點
疑難解答