生產(chǎn)環(huán)境遇到一個 Go 問題,整組人都懵圈了...
大家好,我是煎魚。
前段時間正在瘋狂寫代碼的時候,突然有一個讀者給我提了一個問題,讓我有了一定的興趣:
我還是比較感興趣的,因為是生產(chǎn)環(huán)境、有代碼,且整組人都懵逼的問題。
在征求了小伙伴的意見后,今天分享出來,大家也思考一下原因,一起規(guī)避這個 “坑”。
案例一
代碼示例如下:
- type MyErr struct {
- Msg string
- }
- func main() {
- var e error
- e = GetErr()
- log.Println(e == nil)
- }
- func GetErr() *MyErr {
- return nil
- }
- func (m *MyErr) Error() string {
- return "腦子進(jìn)煎魚了"
- }
請思考一下,這段程序的輸出結(jié)果是什么?
該程序所調(diào)用的 GetErr 方法所返回的是 nil,而外部判斷是 e == nil,因此最終的輸出結(jié)果是 true,對嗎?
輸出結(jié)果如下:
- 2021/04/04 08:39:04 false
答案是:false。
案例二
代碼示例如下:
- type Base interface {
- do()
- }
- type App struct {
- }
- func main() {
- var base Base
- base = GetApp()
- log.Println(base)
- log.Println(base == nil)
- }
- func GetApp() *App {
- return nil
- }
- func (a *App) do() {}
請思考一下,這段程序的輸出結(jié)果是什么?
該程序調(diào)用了 GetApp 方法,該方法返回的是 nil,因此其賦值的 base 也是 nil。因此判斷 base == nil 的最終輸出結(jié)果是
輸出結(jié)果如下:
- 2021/04/04 08:59:00 <nil>
- 2021/04/04 08:59:00 false
答案是:
為什么
為什么,這兩段 Go 程序是怎么回事...也太反直覺了?其背后的原因本質(zhì)上還是對 Go 語言中 interface 的基本原理的理解。
在案例一中,雖然 GetErr 方法確實是返回了 nil,返回的類型也是具體的 *MyErr 類型。但是其接收的變量卻不是具體的結(jié)構(gòu)類型,而是 error 類型:
- var e error
- e = GetErr()
在 Go 語言中, error 類型本質(zhì)上是 interface:
- type error interface {
- Error() string
- }
因此兜兜轉(zhuǎn)轉(zhuǎn)又回到了 interface 類型的問題,interface 不是單純的值,而是分為類型和值。
所以傳統(tǒng)認(rèn)知的此 nil 并非彼 nil,必須得類型和值同時都為 nil 的情況下,interface 的 nil 判斷才會為 true。
在案例一中,結(jié)合代碼邏輯,更符合場景的是:
- var e *MyErr
- e = GetErr()
- log.Println(e == nil)
輸出結(jié)果就會是 true。
在案例二中,也是一樣的結(jié)果,原因也是 interface。不管是 error 接口(interface),還是自定義的接口,背后原理一致,自然也就結(jié)果一致了。
總結(jié)
今天這篇文章,相當(dāng)于是《Go 面試題:Go interface 的一個 “坑” 及原理分析》的變形了,畢竟是生產(chǎn)環(huán)境的代碼改造而來,更貼合真實的實際場景。
下意識的直覺有時候不是絕對正確的,我們要正確的理解 Go 語言中的那些知識點(diǎn),才能更好地實現(xiàn)早下班的理想和愿景。