[AI 中文摘要] On | No syntactic support for error handling

本文為 AI 中文摘要,原始文章:[ On | No ] syntactic support for error handling - The Go Programming Language


文章摘要:Go 語言錯誤處理語法支援的探討與結論

日期: 2025年6月3日
作者: Robert Griesemer

1. 前言:Go 語言錯誤處理的冗餘性問題

Go 語言中常見的錯誤處理模式,即 if err != nil 檢查,長期以來因其冗餘 (verbosity) 而備受詬病。在大量進行 API 呼叫或錯誤處理相對單純(僅向上回傳)的程式中,此模式會淹沒主要邏輯程式碼。例如,一個簡單的 printSum 函數,其十行程式碼中,有六行專用於錯誤檢查與回傳,僅四行執行實際運算。此問題在年度使用者調查中持續名列前茅(僅次於泛型 (generics) 問題,在泛型支援後重回首位)。Go 團隊對此高度重視,並多年來持續尋求解決方案。

2. Go 團隊在錯誤處理語法上的歷史性嘗試

Go 團隊針對錯誤處理的冗餘問題,進行了多次重要的語法改進嘗試:

  • 2.1. checkhandle 機制 (2018年)

    • 由 Russ Cox 正式提出,基於 Marcel van Lohuizen 的草案設計。
    • 該機制引入了 check 關鍵字用於檢查錯誤,handle 區塊用於定義錯誤處理邏輯。
    • 範例:
      func printSum(a, b string) error {
          handle err { return err } // 定義錯誤處理方式
          x := check strconv.Atoi(a) // 若 strconv.Atoi(a) 回傳錯誤,則執行 handle 區塊
          y := check strconv.Atoi(b) // 同上
          fmt.Println("result:", x + y)
          return nil
      }
      
    • 此提案因被認為過於複雜而未被採納。
  • 2.2. try 內建函數提案 (2019年)

    • 作為 check/handle 的簡化版,try 是一個內建函數 (built-in function),若其參數表達式回傳錯誤,try 會使當前函數立即回傳該錯誤。
    • 為評估影響,團隊開發了 tryhard 工具將現有程式碼轉換為使用 try
    • 範例:
      func printSum(a, b string) error {
          x := try(strconv.Atoi(a)) // 若 strconv.Atoi(a) 回傳錯誤,printSum 將回傳該錯誤
          y := try(strconv.Atoi(b)) // 同上
          fmt.Println("result:", x + y)
          return nil
      }
      
    • 此提案因其隱含的控制流程 (control flow) 改變(可能從深層巢狀表達式中提前返回)而引發爭議,最終被放棄。
    • 事後反思,引入新關鍵字而非內建函數,並限制其使用範圍,或許是更佳的途徑。
  • 2.3. ? 運算子提案 (2024年)

    • 由 Ian Lance Taylor 提出,借鏡 Rust 語言的 ? 運算子。
    • 期望藉由參考已建立的機制與符號來取得進展。
    • 非正式使用者研究顯示,多數開發者能正確理解 ? 的含義。
    • 團隊開發了轉換工具並在編譯器中原型化了此功能。
    • 範例:
      func printSum(a, b string) error {
          x := strconv.Atoi(a) ? // 若 strconv.Atoi(a) 回傳錯誤,printSum 將回傳該錯誤
          y := strconv.Atoi(b) ? // 同上
          fmt.Println("result:", x + y)
          return nil
      }
      
    • 此提案同樣迅速引發大量討論與基於個人偏好的微調建議,未能獲得廣泛支持,最終提案被關閉並轉為討論。

3. 社群的持續參與及提案過程的反思

在 Go 團隊嘗試的同時,社群也湧現了大量錯誤處理提案(數以百計),多數為既有概念的變體。Ian Lance Taylor 為此創建了追蹤議題 (umbrella issue) 及 Go Wiki 頁面以彙整相關資訊。

try 提案的失敗促使團隊反思提案流程,Russ Cox 的系列部落格文章指出,過早提出近乎完整的提案並設定緊迫的實施時程,可能減少了社群反饋的空間,影響了結果。

4. Go 團隊的最終決定:暫停追求語法層面的解決方案

歷經多年、多次官方及大量社群提案均未能達成共識後,Go 團隊決定:

  • 在可預見的未來,停止追求針對錯誤處理的語法層級語言變更 (syntactic language changes)。
  • 關閉所有主要涉及錯誤處理語法的現有及未來提案,不再進一步調查。

此決定的依據是 Go 的提案流程 (proposal process) 要求達成普遍共識 (general consensus),而所有錯誤處理提案均未達到此標準。即使是 Go 團隊資深成員內部,目前也未對最佳方案有一致看法。

5. 維持現狀 (Status Quo) 的論點

支持維持當前錯誤處理方式的論點包括:

  • 時機已過: Go 已發展15年,儘管冗餘,但現有的錯誤處理方式運作良好。若早期引入語法糖 (syntactic sugar),爭議可能較小。
  • 避免新的分歧: 引入新語法會使一部分偏好現狀的使用者不滿。
  • 與泛型的差異: 泛型是可選的,而新的錯誤處理語法幾乎會要求所有人都採用,以維持程式碼的慣用風格 (idiomatic)。
  • Go 設計原則: 避免為同一件事提供多種做法。
    • 諷刺的是,短變數宣告 (short variable declarations) := 中的變數重宣告 (redeclaration) 功能,部分是為了解決錯誤檢查序列中 err 變數命名問題而引入。若有更好的錯誤處理語法,此規則或可避免。
  • 實際錯誤處理的影響: 當錯誤被「實際處理」(例如,使用 fmt.Errorf 添加上下文信息)時,樣板程式碼 (boilerplate) 的相對比例會降低。
    • 範例:
      func printSum(a, b string) error {
          x, err := strconv.Atoi(a)
          if err != nil {
              return fmt.Errorf("invalid integer: %q", a) // 錯誤被包裝
          }
          // ...
      }
      
  • 標準庫的輔助: 新的標準庫功能(如 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
          }
          // ...
      }
      
  • 工具的輔助:
    • 撰寫: IDE 和 LLM 輔助的程式碼完成 (code completion) 可簡化重複錯誤檢查的撰寫。
    • 閱讀: IDE 或可提供開關以隱藏錯誤處理程式碼。
    • 除錯 (Debugging): 明確的 if 陳述式更易於設定中斷點 (breakpoint) 或添加調試輸出,而隱藏在 checktry? 後的邏輯則需先轉換回 if 形式。
  • 實際成本考量: 語言變更成本高昂(設計、實施、文件更新、工具調整),而 Go 團隊資源有限,尚有其他優先事項。
  • 部分使用者反饋: 在 Google Cloud Next 2025 等場合,部分 Go 使用者明確表示不希望改變現有錯誤處理方式,並認為隨着對 Go 的熟練度增加,此問題的重要性會降低。

6. 支持變革的論點(雖未被採納)

儘管決定不進行語法變更,但支持變革的論點依然存在:

  • 使用者調查中,錯誤處理冗餘仍是首要抱怨。
  • 關注點或許不應僅是減少字元數,而是透過關鍵字等方式提高預設錯誤處理的可見性,進而提升程式碼品質與安全性。
  • 目前尚不完全清楚問題的核心是語法冗餘,還是良好錯誤處理(建構對開發者和終端使用者都有意義的錯誤)本身的複雜性。

7. 結論與未來展望

鑑於對問題本身及其是否存在缺乏共識,Go 團隊做出務實決定:在可預見的未來停止追求錯誤處理的語法層級語言變更。團隊感謝社群在探討此議題上的巨大投入,這些努力雖未直接改變錯誤處理語法,但也促成了 Go 語言及流程上的其他改進。團隊期望將這份熱情投入到新的機會,使 Go 對所有人更好,並期待未來或許會對錯誤處理問題有更清晰的認識。

Comments