成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go 錯誤處理之殤:Go 團隊的決策與未來

開發
本篇文章,就來詳細介紹下 Go 團隊對于錯誤處理的嘗試、思考和未來的計劃。

Go 語言有很多優秀的語法特性,例如,可以使用 go關鍵字很方便的啟動一個新的協程。但是,Go 同樣也有一些被 Go 開發者所詬病的語法特性,例如:泛型、錯誤處理。其中,錯誤處理,自 Go 誕生以來,一直被很多 Go 開發者詬病,沒有停止過。那么 Go 團隊未來有無計劃改進 Go 的錯誤處理機制?

本篇文章,就來詳細介紹下 Go 團隊對于錯誤處理的嘗試、思考和未來的計劃。

一、持續 15 年的抱怨

Go 語言自誕生以來,關于“錯誤處理太啰嗦”的抱怨就從未停歇。大家對下面這種代碼模式早已耳熟能詳(有人甚至說“看到都頭疼”):

x, err := call()
if err != nil {
        // handle err
}

if err != nil 的檢查往往鋪天蓋地,以至于掩蓋了真正的業務邏輯。API 調用多、錯誤處理又只是簡單返回的程序尤為明顯,最終會出現類似下面的代碼:

func printSum(a, b string) error {
    x, err := strconv.Atoi(a)
    if err != nil {
        return err
    }
    y, err := strconv.Atoi(b)
    if err != nil {
        return err
    }
    fmt.Println("result:", x + y)
    return nil
}

在這個 10 行的函數體里,只有 4 行(兩次調用和最后兩行)真正“干活”,其余 6 行都是噪音。冗長確實存在,因此多年里“錯誤處理太啰嗦”一直穩居 Go 年度用戶調查的抱怨榜首。(一度“缺少泛型”的吐槽超過了它,但在 Go 引入泛型后,錯誤處理又重新登頂。)

Go 團隊十分重視社區反饋,多年來一直在嘗試與社區共同尋找解決方案。

二、三次“官宣”方案,全軍覆沒

Go 團隊先后提出過 3 種錯誤處理方案,但是最終都全軍覆沒:

  • 2018:check / handle 機制;
  • 2019:try 提案;
  • 2024:? 運算符。

1. 2018:check / handle 機制

團隊最早的正式嘗試可追溯到 2018 年。當時 Russ Cox 在所謂的 “Go 2” 計劃中正式描述了這一問題,并在 Marcel van Lohuizen 的草案基礎上提出了可能的解決思路。該設計基于 “check / handle” 機制,內容相當全面。草案還詳細對比了其他語言的各種替代方案。如果你想知道自己的某個錯誤處理點子是否曾被討論過,不妨翻閱 Error Handling — Draft Design[1]。該方案的一段示例代碼如下:

// printSum implementation using the proposed check/handle mechanism.
func printSum(a, b string) error {
    handle err { return err }
    x := check strconv.Atoi(a)
    y := check strconv.Atoi(b)
    fmt.Println("result:", x + y)
    return nil
}

“check / handle” 方案被認為過于復雜,最終擱置。

2. 2019:try 提案

一年后的 2019 年,Go 團隊推出了大幅簡化、如今頗具“傳奇”色彩的 try 提案[2]。它沿用了 check / handle 的思路,但把偽關鍵字 check 改成內置函數 try,并省去了 handle 部分。

為了評估 try 的效果,Go 團隊編寫了一個簡單工具 tryhard[3],可將現有的錯誤處理代碼自動改寫為 try 形式。該提案在 GitHub 議題(#32437[4])中引發了激烈爭論,評論數接近 900 條。

// 使用 try 機制的 printSum 實現
func printSum(a, b string) error {
    // defer 用于在返回前增強錯誤信息
    x := try(strconv.Atoi(a))
    y := try(strconv.Atoi(b))
    fmt.Println("result:", x + y)
    return nil
}

然而,try 會在發生錯誤時直接從外圍函數返回,而且可能出現在深層嵌套的表達式里,導致控制流被“藏”起來。許多人對此難以接受。盡管Go 團隊在此方案上投入巨大,最終仍決定放棄。事后看來,如果當時采用新關鍵字、并借助 go.mod 等機制進行版本隔離,或許會更好。再者,如果把 try 的使用限制在賦值和語句層面,也許能緩解部分擔憂。Jimmy Frasche 最近的一份提案[5]基本回到最初的 check / handle 設計,并針對其缺點進行了改進,正是朝這一方向探索。

try 提案的后續反思催生了 Russ Cox 的系列博文思考 Go 提案流程[6]。其中一個結論是:Go 團隊當時拿出的是“半成品就要落地”的方案,給社區預留的討論空間太小、實施時間表又顯得“咄咄逼人”,因而錯失了更好結果。按照 Go 提案流程:重大變更[7] 中的說法:“事后看來,try 屬于足夠大的變更,其發布的設計應算第二版草案,而不該附帶實施時間表。” 無論流程溝通是否到位,用戶的總體態度都十分明確:不買賬。

當時Go 團隊沒有更好的方案,于是幾年內都未再嘗試修改錯誤處理語法。社區卻靈感不斷,各種提案源源不斷:有的高度相似,有的頗具創意,也有些難以理解或注定不可行。為梳理紛繁的討論,一年后 Ian Lance Taylor 建立了一個 雨傘[8] Issue,匯總改進錯誤處理的所有提案。同時在 Go Wiki[9] 收集相關反饋、討論與文章。社區里還有人獨立整理多年來出現的所有提案——例如 Sean K. H. Liao 的博文 go error handling proposals[10] 就展示了數量之多的令人咋舌。

“錯誤處理太啰嗦”的抱怨依舊(參見《Go 開發者調查 2024 H1 結果》[11])。于是,經過數輪內部方案迭代,Ian Lance Taylor 于 2024 年發布了《使用 ? 減少錯誤處理樣板》[12]。這回借鑒了 Rust 的 ? 運算符。

3. 2024:? 運算符

Go 團隊希望利用已有且被驗證的記號,加上多年積累的經驗,能真正向前邁一步。在一次小型非正式用戶試驗中,開發者看到帶 ? 的 Go 代碼后,絕大多數能正確猜出含義,進一步增強了Go 團隊繼續嘗試的信心。為了直觀評估變化,Ian 編寫了一個工具,把普通 Go 代碼轉換為新語法版本。Go 團隊也在編譯器中做了原型實現。

// printSum implementation using the proposed "?" statements.
func printSum(a, b string) error {
    x := strconv.Atoi(a) ?
    y := strconv.Atoi(b) ?
    fmt.Println("result:", x + y)
    return nil
}

不幸的是,同其他錯誤處理方案一樣,這份新提案也很快被大量評論和各種“微調建議”淹沒,其中不少僅基于個人偏好。Ian 關閉了該提案,并將內容移入 Discussion[13] 以便后續討論、收集更多反饋。稍加修改后的版本雖然評價好了一點[14],但依舊難以獲得廣泛支持。

三、統計:數百份社區提案,仍無共識

這么多年走來,Go 團隊已正式提出 3 份完整方案,社區也提供了字面意義上的數百份(!)變體,結果卻都是雷聲大、雨點小:沒有任何方案能贏得足夠(更別說壓倒性)的支持。Go 團隊現在面對的問題是:下一步該怎么辦?甚至要不要繼續折騰?Go 團隊的結論是:暫時不折騰。

更準確地說,在可預見的未來,Go 團隊不再嘗試通過新增語法來解決錯誤處理的冗長問題。Go 提案流程[15]為這一決定提供了依據:提案流程的目標是在合理時間內達成普遍共識。如果在議題討論中無法形成共識,提案通常會被否決。

進一步地:如果既沒法形成共識,也不能直接否決,最終將由 Go 架構師審閱討論并嘗試在內部達成共識。

四、維持現狀的理由

沒有任何一個錯誤處理提案能夠接近共識,所以它們全部被否決。即便是 Google Go 團隊最資深的成員,目前也未能一致同意最佳的前進方向(也許以后會改變)。但在缺乏強有力共識的情況下,Go 團隊無法合理地繼續推進。

以下是支持維持現狀的一些合理論點:

如果 Go 在早期就為錯誤處理引入了特定的語法糖,今天大概很少有人會為此爭論。但 Go 已經走過了 15 年,這個機會已成過往,Go 現有的錯誤處理方式雖然有時看起來啰嗦,卻完全可用;

換個角度想,假設今天突然發現了完美的解決方案,把它并入語言后,只會把一群不滿(支持改動的人)換成另一群不滿(偏好現狀的人)。當初決定在語言中加入泛型時,情況也類似,不過有一個重要區別:今天沒人被強制使用泛型,而且由于類型推斷的幫助,優秀的泛型庫寫得足夠透明,用戶幾乎感覺不到它們是泛型。相反,如果在語言中直接添加新的錯誤處理語法,幾乎所有人都得開始用它,否則代碼就不夠 idiomatic 了;

不增添額外語法,也符合 Go 的一條設計原則:不要為同一件事提供多種做法。這條規則在一些“高頻”場景(比如賦值)上有例外。具有諷刺意味的是,短變量聲明[16](:=)中允許重聲明變量,正是為了解決錯誤處理帶來的問題:如果不能重聲明,連續的錯誤檢查就需要為每次檢查都使用不同名稱的 err 變量(或額外先聲明一個變量)。那時候也許更好的做法是為錯誤處理提供更多語法支持,那么重聲明規則就無需存在,也不會衍生那么多復雜性;

  • 回到實際的錯誤處理代碼:如果真的在認真處理錯誤,冗長感就會淡化。良好的錯誤處理往往需要在錯誤中添加更多信息。比如用戶調查中經常提到缺少錯誤的堆棧跟蹤。可以通過輔助函數生成并返回增強后的錯誤來解決。舉個(雖有點勉強)的例子,樣板代碼就少多了:
func printSum(a, b string) error {
    x, err := strconv.Atoi(a)
    if err != nil {
        return fmt.Errorf("invalid integer: %q", a)
    }
    y, err := strconv.Atoi(b)
    if err != nil {
        return fmt.Errorf("invalid integer: %q", b)
    }
    fmt.Println("result:", x + y)
    return nil
}

標準庫新增功能也能大幅減少錯誤處理樣板,正如 Rob Pike 2015 年博客 “Errors are values[17]” 所倡導的那樣。例如,在某些場景下可以用 [cmp.Or](https://go.dev/pkg/cmp#Or "cmp.Or") 一次合并多次錯誤:

func printSum(a, b string) error {
    x, err1 := strconv.Atoi(a)
    y, err2 := strconv.Atoi(b)
    if err := cmp.Or(err1, err2); err != nil {
        return err
    }
    fmt.Println("result:", x+y)
    return nil
}

寫代碼、讀代碼、調試代碼是三件截然不同的事。寫一堆重復的錯誤檢查的確枯燥,但如今的 IDE 提供了強大的、甚至是 LLM 輔助的代碼補全,基本的 if err != nil 對它們來說毫無壓力。冗長感在閱讀時代最明顯,工具也能幫忙:比如 IDE 可以設置一個開關,折疊掉所有錯誤處理代碼——類似于已經存在的函數體折疊功能;

調試時,能快速插入一行 println 或在專門的行/位置下斷點非常方便。有了明顯的 if 語句,這些都很容易。但如果所有錯誤處理邏輯都隱藏在某種 check、try 或 ? 機制里,調試前還得先把它改回普通的 if,反而更麻煩,甚至容易引入細微的 bug;

還有現實的考量:想出一個新的錯誤處理語法點子很廉價,所以社區出現了無數提案;但要想出一個經得起推敲的好方案,卻不容易。設計一次語言變動并真正實現,需要大家共同努力。真正的成本在后面:需要改動的海量代碼、要更新的文檔、要適配的工具鏈……綜合來看,語言變更的代價極高,而 Go 團隊人手有限,還有很多其他優先級更高的事情要做。(當然,優先級可以調整,團隊規模也可能增減);

最后,Go 團隊中有些人在 2025 年 Google Cloud Next [18]上有機會與許多 Go 用戶面對面交流。Go 團隊問到的每一個人都堅決認為:不要為了更好的錯誤處理而改變語言。很多人提到,一開始從其它帶有專用錯誤處理語法的語言轉來時,這點最明顯。但寫得越多、越熟練,寫出更地道的 Go 代碼后,這個問題就不再那么重要了。雖然這不是一個足夠具備代表性的樣本,但它提供了不同于 GitHub 討論區的、有價值的用戶視角。

五、支持變更的理由

當然,也有支持變更的合理論點:

  • 在用戶調查中,對更好錯誤處理支持的訴求依然是首要抱怨。如果 Go 團隊真心重視用戶反饋,就應當在未來某個時候對此有所動作。(雖然目前也沒有跡象顯示對語言變更有壓倒性支持。);
  • 也許我們對“減少敲字量”的單一追求本身就是誤導。更好的方法或許是引入一個關鍵字,讓默認的錯誤處理一目了然,同時依然消除 err != nil 樣板代碼。這樣,代碼閱讀者(尤其是審查者)無需“多看幾遍”就能立刻察覺到錯誤已被處理,從而提升代碼質量和安全性——這也正是最初 check/handle 思路的出發點;
  • 我們尚不清楚,問題究竟在于錯誤檢查語法的冗長,還是在于“優質”錯誤處理本身需要更多代碼:比如構造對 API 有意義、對開發者和最終用戶都友好的錯誤信息。這一點值得更深入地研究。

然而,到目前為止,所有針對錯誤處理的嘗試都未獲得足夠的支持。

六、結論:暫緩一切語法層面方案

如果誠實地看待現狀,只能承認:既沒有對問題達成共識,也并非一致認為這是個必須解決的問題。鑒于此,Go 團隊做出以下務實決定:

  • 在可預見的未來,Go 團隊將停止推動任何針對錯誤處理的語法變更;
  • Go 團隊會關閉所有以錯誤處理語法為主要關注點的現有及新提案,不再作進一步評估。

社區在探索、討論和辯論這些議題上付出了巨大努力。雖然這些努力未能改變錯誤處理語法,卻為 Go 語言及其流程帶來了諸多改進。或許將來某個時刻,Go 團隊會對錯誤處理有更清晰的認識。在那之前,Go 團隊期待把這份熱情投入到讓 Go 變得更好的其他新機遇中。

責任編輯:趙寧寧 來源: 令飛編程
相關推薦

2014-11-17 10:05:12

Go語言

2021-04-29 09:02:44

語言Go 處理

2025-03-31 00:29:44

2021-09-13 07:53:31

Go錯誤處理

2022-09-05 08:55:15

Go2提案語法

2025-06-30 09:49:11

2021-09-27 15:33:48

Go 開發技術

2023-10-26 15:49:53

Go日志

2020-12-17 06:25:05

Gopanic 模式

2021-09-27 23:28:29

Go多協程并發

2021-09-27 10:04:03

Go程序處理

2023-03-10 08:48:29

2025-06-06 02:15:00

2024-02-28 08:54:57

switchGo錯誤

2022-07-13 08:53:28

函數Go語言

2025-02-24 09:30:15

2022-08-01 08:48:39

Go代碼接口

2022-07-08 08:55:56

Go函數模型

2022-10-24 08:55:13

Go工具鏈開發者

2022-12-12 08:53:53

Go版本方式
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲成人一区二区三区 | 亚洲黄色一级 | 久久久久久久久久久国产 | 欧美视频一区二区三区 | 日本精品视频在线 | 日韩中文一区 | 国产精品国产成人国产三级 | 成人三级在线观看 | 国产精久久久久久久 | 日韩欧美黄色 | 成人特区 | 久久新视频 | 人人干在线 | 欧美视频 亚洲视频 | 一级片视频免费观看 | 免费小视频在线观看 | 成人精品一区二区 | 视频二区在线观看 | 色噜噜亚洲男人的天堂 | 国产精品欧美一区二区三区 | 国产精品一区二区欧美黑人喷潮水 | 久久精品二区 | 国产乱码久久久久久一区二区 | 亚洲一区二区三区四区五区午夜 | 日日想夜夜操 | 一区二区免费高清视频 | 九九伊人sl水蜜桃色推荐 | 午夜精品视频在线观看 | 亚洲视频 欧美视频 | 久久久久久黄 | 精品一区二区av | 婷婷激情综合 | 丝袜天堂 | 日韩精品一区二区三区在线播放 | 国产激情一区二区三区 | 免费久久网站 | 美女视频黄色片 | 国产日韩欧美激情 | 成人在线免费电影 | 亚洲精品乱码久久久久久按摩观 | 狠狠的操 |