2 回答

TA貢獻1858條經驗 獲得超8個贊
這是一個完全沒有問題的問題,因為在實踐中,如果將一個nil值存儲在接口變量中,使該變量成為非nil,則可能會造成混淆。這是 https://golang.org/doc/faq#nil_error 每個人都會被這個問題咬幾次,直到你了解到包含nil變量的接口值本身不再是nil。這有點像只包含 nils 但本身是 non-nil 的。var s = []*int{nil, nil, nil}s
從技術上講(從語言設計的角度來看),您可以引入幾個“nils”,例如 用于指針、接口、函數和通道。(有點夸張)。有了這個,你可以有:nilnullnoopvac
type T whatever
func (t *T) Error() string // make *T implement error
var err error // interface variable of type error
print(err == null) // true, null is Zero-Value of interface types
print(err == nil) // COMPILER ERROR, cannot compare interface to nil (which is for pointers only
var t *T = nil // a nil pointer to T
err = t // store nil *T in err
print(err == null) // false err is no longer null, contains t
您甚至可以刪除編譯器錯誤:
err = t // store nil *T in err
print(err == null) // false, err contains t
print(err == nil) // "deep" comparison yielding true
err = &T{} // store non-nil *T in err
print(err == null) // still false
print(err == nil) // false, value inside err is no longer nil
您還可以為 nil 定義一個默認類型,例如 這樣你就可以像編寫非類型常量 3.141 的默認類型是 float64 一樣進行編寫。但是,nil的這種默認類型將是任意的,根本沒有幫助(如我的示例所示; 不常見)。*[]chan*func()stringx := nilf := 3.141*[]chan*func()string
如果我沒記錯的話,在golang-nuts mailng列表中有一個關于這個主題的更長的討論,其中討論了這個設計的合理性。它歸結為:“具有多種含義而不是常量的實際現實生活問題是微小的(基本上只是包含nil的錯誤類型的變體不是nil)。這個小問題的“解決方案”將使語言復雜化(例如,通過為接口類型的零值引入一個文字)。告訴人們包含 nil 的接口值本身不再是 nil 可能比為接口類型引入類型化的 nils 或 null 更簡單。nilnull
在10多年的Go編程中,我實際上從未考慮過一個字面上被類型化或非類型化或常量或其他什么。您可能正在引用的文章正在構建純粹的學術性,但實際上在實踐中沒有問題,這是一個微小的設計決策,即指針,切片,映射,通道和函數類型的所有零值只有一個文本。nilnil
補遺
首先,即使 nil 在紙面上是無類型的,它也采用錯誤類型(通過實現 Error 接口和 Go 的 duck 類型),以便符合 might_error 的返回類型簽名。
這是對所發生事情的完全錯誤的描述。
如果你寫
func f() (r int) { return 7 }
則 7 被分配給 int 類型的 r,f 返回。這有效,因為可以將 7 分配給 int。
在
func might_error() (int, error) { return 1, nil }
同樣的情況是,error(接口類型)的第二個返回變量設置為 nil,因為 nil 可以分配給任何接口類型,就像您可以將 nil 分配給任何指針類型或任何函數類型一樣。
這與“實現錯誤接口與Go的鴨子類型”無關。絕對不是。nil 根本沒有實現錯誤接口。任何接口值都可以為零,就像可以是任何函數值、切片值或通道值一樣。將chan設置為nil基本上“清除”通道變量,這并不意味著nil以某種方式“實現通道接口”。您似乎將幾種類型的零值以及如何通過分配nil與實現接口來設置它們混為一談。所有這些基本上與是否鍵入nil無關。源代碼中的文字 os 重載,通常可以認為只是表示類型的零值。nil

TA貢獻1831條經驗 獲得超10個贊
你的例子不會編譯(因為不需要返回任何東西),當你進入一種語言的挑剔和細節領域時,你所有的例子都應該真正起作用。??main
我目前的想法是,在函數的類型簽名需要它的情況下,Error接口被注入到nil的特定實例中......
這不太對。Go 規范告訴我們這是如何工作的:
有三種方法可以從具有結果類型的函數返回值:
返回值可以在“return”語句中顯式列出。每個表達式都必須是單值表達式,并且可以分配給函數結果類型的相應元素。[截圖示例]
這是您在示例程序中使用的方法(我清理了一下以在Go Playground中編譯和運行)。表達式:
return 1, nil
方法:
unnamed_return_variable_1 = 1unnamed_return_variable_2 = nil
其中,兩個未命名的返回變量實際上沒有這些名稱(畢竟它們是未命名的),但確實有類型,這要歸功于函數的聲明。所以
return 1, nil
根本不像:
x, err := 1, nil
但更像是:
var x int; var err error; x, err = 1, nil
如您所見,這也是非常有效的。
是什么促使了這些設計選擇?
為此,你必須問問設計師。
未鍵入的 0 不會引起這種焦慮。
但是,非類型化常量具有默認類型:0
非類型化常量具有默認類型,該類型是在需要類型化值的上下文中(例如,在短變量聲明(例如,沒有顯式類型)中將常量隱式轉換為的類型。非類型化常量的默認類型分別為 、 、 或,具體取決于它是布爾值、符文、整數、浮點數、復數還是字符串常量。
i := 0
bool
rune
int
float64
complex128
string
預先聲明的標識符沒有類型,甚至沒有默認類型。它只是有一系列特殊規則,允許它在各個地方使用。nil
非語言規范的思考方式
雖然這不是它的定義方式(它是由Go規范定義的),但我建議這樣考慮這個問題:
預先聲明的標識符1 是非類型化的。
nil
存在類型化的 nil 值。任何足夠像指針的東西都可以是 nil,也可以是 non-nil。例如,任何變量都可以是指向int-nil的指針,就像未初始化的變量一樣。此 nil 不是 nil “關鍵字”(預先聲明的標識符,見腳注);這是一個完全不同的“無”,就像布魯斯(班納)是一個與布魯斯(韋恩)完全不同的人。
*int
接口值由兩部分組成:類型和該類型的值。類型可以為零!這是另一種 nil,不同于 nil(未鍵入的“關鍵字”)和 nil(某些特定 pointer-y 類型的某些特定 nil)。如果類型為 nil,則該值也可以為 nil。如果兩者都為 nil,則接口值比較等于 <nil,nil>對,當需要與接口值進行比較時,未鍵入的“關鍵字”nil 會變成該對。如果任何一個為非零,則比較表明它們不相等。硬件實現是接口的兩個部分都是零:如果任何一個部分為非零,則整個事物為非零,因此“不是零”。
當上下文提供了足夠的信息時,將從 nil-the-“關鍵字”(預先聲明的標識符)到適當的硬件樣式零值進行轉換。分配給某個變量(甚至是未命名的變量)可提供上下文。分配給某些位置參數可提供上下文。嘗試使用簡短聲明無法提供上下文,因此會出現錯誤。
1 個一般來說,Go更喜歡預先聲明的標識符到關鍵字,因此等等實際上并不是關鍵字。這也排除了關鍵字集。盡管如此,有些東西——比如布爾常量和,或者預先聲明的標識符的神奇行為,只能通過不覆蓋預先聲明的標識符然后使用它來訪問。也就是說,如果您命名某些內容,則在該名稱超出范圍之前,您將無法獲得iota樣式功能。這些事情的行為方式通常通過其他語言的關鍵字來處理。如果你傾向于思考,比如說,C++或Java,你可能想不斷提醒自己,這些不是關鍵字,即使它們聞起來像它們。false
true
nil
false
true
iota
iota
- 2 回答
- 0 關注
- 162 瀏覽
添加回答
舉報