在近日新的try 提案《proposal: Go 2: error handling: try statement with handler#[1]》在社區引發了熱議,再戰社區! ! !
今天煎魚跟大家一起打開來看看,這能把 Go 錯誤處理機制給掀開重整不。
來自PingCAP 的提案作者@Greg Weber 會做這事基於兩個因素,一個是在《Go Developer Survey 2022 Q2 Results[2]》中明確提到。
隨著 Go1.18 泛型的發布,原先矛盾最深的泛型已經得到一個初步的解決方案。在社區調查上,開發者在使用 Go 時面臨的最大挑戰已經轉移到了錯誤處理上,需要投入精力去 “解決” 它。
另外一個因素就是眾所皆知的,Go 錯誤處理程式碼比較繁瑣,常被工程師們戲稱一個 Go 工程裡有 30% 都 if err = nil。
如下程式碼:
_, err := f() if err != nil { ... } _, err = r() if err != nil { ... } _, err = w() if err != nil { ... }
希望讓其更優雅。也有許多小夥伴認同這個設計,確實是簡單、直覺的處理,在社區形成了角力。
本提案中所提到的解決方案,是增加一個新語句try ,以此達到簡潔的錯誤處理的作用,讓if err != nil 的處理絲滑起來。
如下程式碼:
try err, handler
編譯器翻譯後產生的程式碼:
if err != nil { return handler(err) }
在函數中可以如下:
func(args...) (rtype1, rtypes..., rtypeN, error) { try err, handler ... }
翻譯後產生的程式碼:
func(args...) (rtype1, rtypes..., rtypeN, error) { if err != nil { return Zero(rtype1), Zeros(rtypes...)..., Zero(rtypeN), handler(err) } ... }
也可以只針對if err != nil 處理。如下程式碼:
try err
翻譯後產生的程式碼:
if err != nil { return err }
不會呼叫不存在的 handler 進行處理,將會直接回傳。三行(if err != nil 的邏輯)直接變成 3 個字(try)。
不想寫函數,也可以直接:
x, err := f() try err, fmt.Errorf("f fail: %w", err)
針對defer try 的場景可以如下:
func CopyFile(src, dst string) error { defer try func(err error) error { return fmt.Errorf("copy %s %s: %w", src, dst, err) } ... }
入參是比較靈活的,作者希望它是泛型,這樣能夠適配各種場景的要求。
针对本提案,原作者给出了各类使用场景的示例。如下代码:
import ( "fmt" ) // This helper should be defined in the fmt package func Handlew(format string, args ...any) func(error) error { return func(err error) error { args = append(args, err) return fmt.Errorf(format+": %w", args...) } } // This helper should be defined in the fmt package func Handlef(format string, args ...any) func(error) error { return func(err error) error { args = append(args, err) return fmt.Errorf(format+": %v", args...) } } func valAndError() (int, error) { return 1, fmt.Errorf("make error") } func newGo() (int, error) { x, err := valAndError() try err // Common formatting functions will already be provided i := 2 x, err = valAndError() try err, Handlew("custom Error %d", i) // Using a custom error type // For convenience the error type can expose a method to set the error x, err = valAndError() try err, TheErrorAsHandler(i) } type TheError struct{ num int err error } func (t TheError) Error() String { return fmt.Sprintf("theError %d %v", t.num, t.err) } func TheErrorAsHandler(num int) func(err) TheError { return func(err error) TheError { return theError{ num: i, err: err } } }
另外在日常的 Go 工程中,提案作者认为 CopyFile 函数是新提案语句的一种很好的实践。为此基于 try-handler 进行了一版改造和说明。
如下代码:
// This helper can be used with defer func handle(err *error, handler func(err error) error) { if err == nil { return nil } *err = handler(err) } func CopyFile(src, dst string) (err error) { defer handle(&err, func(err error) error { return fmt.Errorf("copy %s %s: %w", src, dst, err) }) r, err := os.Open(src) try err defer r.Close() w, err := os.Create(dst) try err, func(err error) error { os.Remove(dst) // only if Create fails return fmt.Errorf("dir %s: %w", dst, err) } defer w.Close() err = io.Copy(w, r) try err err = w.Close() try err return nil }
引入 try-hanlder 后,能够做到:
在这个新提案中,一旦实施,就可以减少如下代码的编写:
if err != nil { return ... }
在代码编写上会节省一些行数,且可以为错误处理机制引入一些新的 ”操作“,这是该提案的优势。
但是从 Go 开发者的角度而言,会引入一些新的副作用,例如:初学者的学习成本、Go 工具链的改造、程序理解的复杂度增加。
另外新的语句,似乎比较难与 Go1.13 引入的 error.Is 和 As 有较好的相关联性。如果是做一个第三方用户库引入倒可以,但若是作为标准进入 Go 源代码中,似乎又有些格格不入(提案作者希望进入)。
看了那么多提案,Go 错误处理机制的 ”升级“,似乎陷入了手心手背都是肉的阶段...
以上是Go try 新提案可靠嗎?想簡化錯誤處理了的詳細內容。更多資訊請關注PHP中文網其他相關文章!