前幾天看 Reddit 社羣裡的討論,發現 Go 這一門程式語言,錯誤處理永遠是討論的議題之一。本著追蹤網友腦洞 Proposal 的基礎上,週末看到個被反對比較多的 Go2 錯誤處理提案。
今天結合分享給大家,好的壞的都可以圍觀一下。
問題背景
在 Go 這門程式語言中,錯誤處理機制主要是依賴於 if err != nil
的方式。因此在對函式做一定的封裝後。
程式碼最終常呈現出以下樣子:
jy1, err := GetFoo() if err != nil { return err } jy2, err := SliceTheBar(varFoo) if err != nil { return err } err := CheckBarSlice(sliceBar) if err != nil { return err } ...
有部分開發者會認為這比較的醜陋、混亂且難以閱讀。
有人戲稱一個 Go 工程裡有 60% 的程式碼是 if err != nil
,為此我見過直接用 panic
來做錯誤處理的團隊。
新提案:用 #err 作為識別符號
提案的提出者 @mainjzb,主要的設計目標是:將 #
作為錯誤符號,格式上將 #@
作為處理程式錯誤符號。幫助開發者閱讀程式碼並簡化程式碼。
原本 Go 錯誤處理方式,如下老程式碼:
n, err := io.Write(x) n, _ := io.Write(x) n, err := io.Write(x) if err != nil { return 0, err } n, err := io.Write(x) if err != nil { return 0, fmt.Error("tcp closed: %w", err) } n, err := io.Write(x) if err != nil{ panic(err) }
使用上述提案後的錯誤標識改造後,新的程式碼如下:
// 1. err as value n := io.Write(x) #err // 2. ignore error n := io.Write(x) #@ignore // 3. return error immediately、 n := io.Write(x) #@done // 4. wrap additional information n := io.Write(x) #@wrap("tcp closed: %w") // 5. panic err n := io.Write(x) #@must
#err
識別符號:err 變數作為值,一切與以前 error 一樣。只是變成了#err
的標識用法。#@ignore
識別符號:使忽略錯誤變得比以前更易讀,也可以用附加的描述資訊便於開發者閱讀。#@done
識別符號:直接返回錯誤資訊。很多時候(特別是在庫中),只需要返回錯誤,無需執行任何操作。例如:url.parseAuthority
。#@wrap
識別符號:在 error 上附帶更多的錯誤資訊,例如:#@wrap
等於#@wrap("io.Wirite err:")
。#@must
識別符號:這個識別符號可以在產生錯誤時,直接發生 panic 事件。
總結
這個提案的作者有多門程式語言經驗,本次提出的新提案,很明顯是瞄著解決 Go 這門程式語言中的 if err != nil
的不斷重複的程式碼內容的方向去的。
雖然原提案作者另闢蹊徑,透過增加 #err
這類識別符號來直接扭轉錯誤處理,解決了大量重複 err 程式碼。
但最終與 Go 語言的其他部分過於不適。已經被 ban 了。謹記:想要最佳化 GO 的 if err != nil
還得要考慮整體適合度,不能一廂情願。