<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你對 Go 錯誤處理的 4 個誤解!

          共 5423字,需瀏覽 11分鐘

           ·

          2021-09-30 17:12

          Go 語言中錯誤處理的機制一直是各大 Gopher 熱議的問題甚至一直有人寄望 Go 支持 throwcatch 關(guān)鍵字,實現(xiàn)與其他語言類似的特性。社區(qū)里的討論也從未停過。

          今天煎魚帶大家了解幾個 Go 語言的錯誤處理中,大家最關(guān)心,也是最容易被誤解、被嫌棄的問題:

          1. 為什么不支持 try-catch?
          2. 為什么不支持全局捕獲的機制?
          3. 為什么要這么設(shè)計錯誤處理?
          4. 未來的錯誤處理機制會怎么樣?

          落寞的 try-catch

          在 Go1 時,大家知道基本不可能支持。于是打起了 Go2 的主意。為什么 Go 就不能支持 try-catch 組合拳?

          上一年宣發(fā)了 Go2 的構(gòu)想,所以在 2020 年就有小伙伴乘機提出過類似 《proposal: Go 2: use keywords throw, catch, and guard to handle errors[1]》的提案,這可是其他語言都支持的,Go 語言怎么了?

          下面來自該提案的演示,Go1 的錯誤處理:

          type data struct {}

          func (d data) bar() (string, error) {
              return "", errors.New("err")
          }

          func foo() (data, error) {
              return data{}, errors.New("err")
          }

          func do () (string, error) {
              d, err := foo()
              if err != nil {
                  return "", err
              }

              s, err := d.bar()
              if err != nil {
                  return "", err
              }

              return s, nil
          }

          新提案所改造的方式:

          type data struct {}

          func (d data) bar() string {
              throw "", errors.New("err")
          }

          func foo() (d data) {
              throw errors.New("err")
              return
          }

          func do () (string, error) {
              catch err {
                  return "", err 
              }

              s := foo().bar()
              return s, nil
          }

          不過答復非常明確,@davecheney 在底下回復“以最強烈的措辭,不(In the strongest possible terms, no)”。這可讓人懵了圈,為什么這么硬呢?

          其實 Go 官方早在《Error Handling — Problem Overview[2]》提案早已明確提過,Go 官方在設(shè)計上會有意識地選擇使用顯式錯誤結(jié)果和顯式錯誤檢查

          結(jié)合《language: Go 2: error handling meta issue[3]》可得知,要拒絕 try-catch 關(guān)鍵字的主要原因是:

          • 涉及到額外的流程控制,因為使用 try 的復雜表達式,會導致函數(shù)意外返回。
          • 在表達式層面上沒有流程控制結(jié)構(gòu),只有 panic 關(guān)鍵字,它不只是從一個函數(shù)返回。

          說白了,就是設(shè)計理念不合,加之實現(xiàn)上也不大合理。在以往的多輪討論中早已被 Go 團隊拒絕了。

          反之 Go 團隊倒是一遍遍在回答這個問題,已經(jīng)不大耐煩了,直接都整理了 issues 版的 FAQ 了。

          想捕獲所有 panic

          在 Go 語言中,有一個點,很多新同學會不一樣碰到的。那就是在 goroutine 中如果 panic 了,沒有加 recover 關(guān)鍵字(有時候也會忘記),就會導致程序崩潰。

          又或是以為加了 recover 就能保障一個 goroutine 下所派生出來的 goroutine 所產(chǎn)生的 panic,一勞永逸。

          但現(xiàn)實總是會讓人迷惑,我經(jīng)常會看到有同學提出類似的疑惑:

          來自 Go 讀者交流群

          這時候,有其他語言經(jīng)驗的同學中,又有想到了一個利器。能不能設(shè)置一個全局的錯誤處理 handler。

          像是 PHP 語言也可以有類似的方法:

          set_error_handler();
          set_exception_handler();
          register_shutdown_function();

          顯然,Go 語言中并沒有類似的東西。歸類一下,我們聚焦以下兩個問題:

          1. 為什么 recover 不能捕獲更上層的 panic?
          2. 為什么 Go 沒有全局的錯誤處理方法?

          源碼層面

          如果是講設(shè)計的話,其實只是通過 Go 的 GMP 模型和 defer+panic+recver 的源碼剖析就能知道了。

          本質(zhì)上 defer+panic 都是掛載在 G 上的,可查看我以前寫的《深入理解 Go panic and recover[4]》,你會有更多深入的理解。

          設(shè)計思想

          在本文中我們不能僅限于源碼,需要更深挖,Go 設(shè)計者他的思想是什么,為什么就是不支持?

          在 Go issues 中《proposal: spec: allow fatal panic handler[5]》、《No way to catch errors from goroutines automatically[6] 》分別的針對性探討過上述問題。

          Go 團隊的大當家 @Russ Cox 給出了明確的答復:Go 語言的設(shè)計立場是錯誤恢復應該在本地完成,或者完全在一個單獨的進程中完成

          這就是為什么 Go 語言不能跨 goroutines 從 panic 中恢復,也不能從 throw 中恢復的根本原因,是語言設(shè)計層面的思想所決定

          在源碼剖析時,你所看到的整套 GMP+defer+panic+recover 的機制機制,就是跟隨著這個設(shè)計思想去編寫和發(fā)展的。

          設(shè)計思想決定源碼實現(xiàn)。

          建議方式

          從 Go 語言層面去動搖這個設(shè)計思想,目前來看可能性很低。至少 2021 年的現(xiàn)在沒有看到改觀。

          整體上會建議提供公共的 Go 方法去規(guī)避這種情況。參考 issues 所提供的范例如下:

          recovery.SafeGo(logger, func() {
              method(all parameters)
          })

          func SafeGo(logger logging.ILogger, f func()) {
           go func() {
            defer func() {
             if panicMessage := recover(); panicMessage != nil {
              ...
             }
            }()

            f()
           }()
          }

          是不是感覺似曾相識?

          每家公司的內(nèi)部庫都應該有這么一個工具方法,規(guī)避偶爾忘記的 goroutine recover 所引發(fā)的奇奇怪怪問題。

          也可以參照建議,利用一個單獨的進程(Go 語言中是 goroutine)去統(tǒng)一處理這些 panic,不過這比較麻煩,較少見。

          未來會如何

          Go 社區(qū)對 Go 語言未來的錯誤處理機制非常關(guān)心,因為 Go1 已經(jīng)米已成炊,希望在 Go2 上解決錯誤處理機制的問題。

          期望 Go2 核心要處理的包含如下幾點(#40432):

          1. 對于 Go2,我們希望使錯誤檢查更加輕便,減少專門用于錯誤檢查的 Go 程序代碼的數(shù)量。我們還想讓寫錯誤處理更方便,減少程序員花時間寫錯誤處理的可能性。
          2. 錯誤檢查和錯誤處理都必須保持明確,即在程序文本中可見。我們不希望重復異常處理的陷阱。
          3. 現(xiàn)有的代碼必須繼續(xù)工作,并保持與現(xiàn)在一樣的有效性。任何改變都必須與現(xiàn)有的代碼相互配合。

          為此,許多人提過不少新的提案...很可惜,截止 2021.08 月底為止,有許多人試圖改變語言層面以達到這些目標,但沒有一個新的提案被接受。

          現(xiàn)在也有許多變更并入 Go2 提案,主要是 error-handling 方面的優(yōu)化。

          大家有興趣可以看看我之前寫的:《先睹為快,Go2 Error 的掙扎之路》,相信能給你帶來不少新知識。

          總結(jié)

          看到這里,我們不由得想到。為什么,為什么在 21 世紀前者已經(jīng)有了這么多優(yōu)秀的語言,Go 語言的錯誤處理機制依然這么的難抉擇?

          顯然 Go 語言的開發(fā)團隊是有自己的設(shè)計哲學和思想的,否則 “l(fā)ess is more” 也不會如此廣泛流傳。設(shè)身處地的理解 Go 官方的想法,而不是一味地單向理解,會對我們未來的編程之路更好。

          當然,這存在著一系列既要也要的問題,不好處理。歡迎大家關(guān)注煎魚,后續(xù)我們也可以面向 Go 后續(xù)的錯誤處理持續(xù)的關(guān)注和討論!

          參考資料

          [1]

          proposal: Go 2: use keywords throw, catch, and guard to handle errors: https://github.com/golang/go/issues/40583

          [2]

          Error Handling — Problem Overview: https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md

          [3]

          language: Go 2: error handling meta issue: https://github.com/golang/go/issues/40432

          [4]

          深入理解 Go panic and recover: https://eddycjy.com/posts/go/panic/2019-05-21-panic-and-recover/

          [5]

          proposal: spec: allow fatal panic handler: https://github.com/golang/go/issues/32333

          [6]

          No way to catch errors from goroutines automatically: https://github.com/golang/go/issues/20161


          關(guān)注煎魚,吸取他的知識 ??



          你好,我是煎魚。高一折騰過前端,參加過國賽拿了獎,大學搞過 PHP。現(xiàn)在整 Go,在公司負責微服務架構(gòu)等相關(guān)工作推進和研發(fā)。

          從大學開始靠自己賺生活費和學費,到出版 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領(lǐng)域最有觀點專家)榮譽,點擊藍字查看我的出書之路

          日常分享高質(zhì)量文章,輸出 Go 面試、工作經(jīng)驗、架構(gòu)設(shè)計,加微信拉讀者交流群,記得點贊!

          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99热0| 国产69精品久久久久久久久久 | 国产精品啪啪啪 | 欧美久久国产精品 | 欧美视频一区二区三区四区 |