?go語言使用error來處理錯誤,用panic和recover來處理異常
error
go語言的錯誤處理有兩個發展階段,以go1.13版本為分水嶺,在1.13版本之前,標準庫對error的支持非常有限,僅有errors.New()和fmt.Errorf()兩個函數來構造errorString實例。在這個階段,error處理存在一個非常大的問題:傳遞錯誤時,使用fmt.Errorf()傳遞捕獲的error并為error增加上下文信息時,原error將和上下文的描述信息混雜在一起,導致原始的error信息丟失。
在go1.13版本,Go官方加強了對errors的支持,在進行錯誤傳遞的時候,采用鏈式傳遞的方式,將同一上下文的error鏈接起來,并且保留原始的error信息。通過在fmt.Errorf()中增加一個格式動詞%w,來創建鏈式的error,并且提供errors.Unwarp()來拆解error。
創建錯誤的兩種方式:
-
errors.New(str string)
-
fmt.Errorf("format string",a ...any)
我們可以通過以上兩種方式來創建錯誤類型,第一種創建出來的是stringError,第二種方式創建出來的類型,取決于傳入的格式動詞,在1.13版本之前只能夠創建stringError類型的錯誤。在1.13版本之后,通過傳入格式動詞%w,可以創建出warpError類型的錯誤,我們需要注意的是,傳入的%w應該和傳入的err來配對。
defer
不僅函數正常返回會執行被defer延遲的函數,函數中任意一個return語句、panic語句均會觸發延遲函數。
defer的三大規則:
-
延遲函數的參數在defer語句出現時就已經確定了,延遲函數體中引用的變量,在編譯的會綁定主函數中的對應變量,會獲取變量的最終值。【對于指針函數的變量,復制的是地址值】
-
延遲函數按后進先出的順序執行,即先出現的defer最后執行
-
延遲函數可能會操作主函數的具名返回值。【return的返回操作并不是原子性的,而是分為兩個步驟:一是設置返回值,二是執行跳轉,defer語句發生在跳轉之前】(分析方向,先判斷返回值的類型,是匿名還是具名,只有在defer語句中,對具名返回值進行更改的時候,才會修改返回值)
panic
panic的工作機制:每一個協程中都維護一個defer鏈表,執行過程中每遇到一個defer語句都會創建一個defer實例,并將這一個defer實例插入鏈表,函數退出時取出本函數創建的defer實例并執行。panic發生時,實際上是把程序的流程轉向了這一個defer鏈表,程序專注于消費鏈表中的defer函數,當鏈表中的defer函數被消費完,再觸發程序的退出。
我們在程序中一些可以預料到的、業務上的錯誤,可以通過定義err進行異常處理,處理err時,我們的程序是正常的,按照我們編寫的邏輯順序執行的。 而panic則用來處理不可預料到的錯誤和進行危險操作時觸發panic。一旦程序發生panic,那么panic所在的函數體之后的代碼將不會再執行,轉而遞歸執行當前協程所有的defer函數,當前協程中的所有defer處理完成后,觸發程序退出。如果panic執行過程中任意一個defer函數執行了recover(),那么panic的處理流程就會中止。程序繼續執行上游函數中的代碼。
recover
內置函數recover()用于消除panic并使程序回到正常的執行流程中。
recover的返回值是捕獲到的panic()函數的參數
recover()執行之后,程序的執行流程轉向上游函數,并且上游函數感知不到panic的發生。而發生panic的函數后面的代碼不會被執行。【一旦發生驚慌,函數的執行流程就變了】
recover()函數必須直接位于defer函數中才有效。defer func() { recover() }()