[go語言]使用defer和有名傳回值,實現初始化失敗的自動復原

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。稍微複雜一些的程式的初始化會涉及到多個模組,任何一個模組初始化失敗以後,就應該把已經初始化的其他模組一一復原。這容易使得初始化的錯誤處理比較冗長,包含重複代碼;或者執行路徑跳轉不容易讀懂,也容易出錯。用go語言提供的defer和有名傳回值的機制,可以比較容易地解決這個問題,讓初始化錯誤處理簡潔清晰。
先看看一種常見的錯誤處理代碼:func Initialize() error {
var err errorerr = init_a()if err != nil {
return err
}err = init_b()if err != nil {
uninit_a()
return err
}
err = init_c()
if err != nil {
uninit_b()
uninit_a()
return err
}
err = init_d()
if err != nil {
uninit_c()
uninit_b()
uninit_a()
return err
}
return nil
}可以看出來,這樣的處理非常的麻煩,uninit_a()這樣的語句要寫一次又一次。
那麼試試用goto:func Initialize() error {
var err errorerr = init_a()if err != nil {
goto _err
}err = init_b()if err != nil {
goto _ua
}
err = init_c()
if err != nil {
goto _ub
}
err = init_d()
if err != nil {
goto _uc
}
_uc:
uninit_c()
_ub:
uninit_b()
_ua:
uninit_a()
_err:
return err
_exit:
return nil
}這樣比前一種寫法好一些,不用重複地寫uinit_a(),uinit_b()...了。但是用goto也有很明顯的缺點:
  1. label太多,看的眼花
  2. 使用goto,執行流程一下子跳走了,導致思維中斷和跳躍
  3. 變數的初始化全部要放到函數的開始位置,不能放在使用它的地方,不然會編譯報錯“jumps over declaration”。
這麼說goto也不是一個好的方案。有人說可以定義一個函數數組,在迴圈裡做初始化和出錯復原:func Initialize() error {
inits := []func() error {init_a, init_b,init_c, init_d}uinits := []func() error {uninit_a,uninit_b, uninit_c, uninit_d}for i := 0; i <len(inits); i++ {
err := inits[i]()if err != nil {
for j := i - 1; j >= 0;j-- {
uninit[j]()
}
return err
}
}return nil
}這個方案看上去很簡潔很漂亮。但是也有一個致命的缺點:它不是一個通用的解決方案。因為init_a(),init_b(),...它們的函數簽名不一定一樣,甚至有可能有依賴:比如init_b()需要一個變數作為參數,這個變數由init_a()返回。這個時候就沒辦法統一處理了,所以這個方案也不可行。
但是用defer和有名傳回值就可以很好的處理這種問題:func Initialize() (err error) { //注意這裡定義了一個err做為傳回值變數
rollback := func(uninit func()){
if err != nil {
uninit()
}
}err = init_a()if err != nil {
return err
}
defer rollback(uninit_a)
err = init_b()
if err != nil {
return err
}
defer rollback(uninit_b)
err = init_c()
if err != nil {
return err
}
defer rollback(uninit_c)
return init_d()
}
很簡潔很美吧?可能還有一個小小的疑問:反初始化函數的參數簽名不一致怎麼辦呢?回答是:
  1. 絕大多數情況下反初始化函數的參數簽名都是一樣的
  2. 即使個別不一樣,在defer的時候也可以用閉包封裝一下,沒有什麼負擔。例如:
defer rollback(func(){uninit_b(100)})

就是這麼簡單。
在golang-nut(需要翻牆)上討論相關問題的時候,得到了Ian Lance Taylor,minux, NigelTao以及其他人的熱情指點和協助,非常感謝他們以及熱情和充滿活力的golang社區。四月份平民指出文中最後一段代碼有誤(現已修正),在此也表示感謝。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.