golang 錯誤處理與異常

2021-08-31 01:25:36 字數 3630 閱讀 5985

golang 中的錯誤處理的哲學和 c 語言一樣,函式通過返回錯誤型別(error)或者 bool 型別(不需要區分多種錯誤狀態時)表明函式的執行結果,呼叫檢查返回的錯誤型別值是否是 nil 來判斷呼叫結果。

golang 中內建的錯誤型別 error 是乙個介面型別,自定義的錯誤型別也必須實現為 error 介面,這樣呼叫總是可以通過 error() 獲取到具體的錯誤資訊而不用關心錯誤的具體型別。標準庫的fmt.errorferrors.new可以方便的建立 error 型別的變數。

type

error

inte***ce

c 語言的語法限制函式只能有乙個返回值,函式的執行結果和執行成功時需要返回的資訊都放到這個返回值裡,具體的錯誤資訊需要呼叫額外的介面獲取。比如 c 標準庫函式讀取檔案的函式 read 返回 -1 時表示讀取錯誤,具體錯誤資訊需要通過 errno 獲取,返回 0 時,表示 eof ,返回正整數時,表示成功讀取到的位元組數。golang 的多返回值語法糖避免了這種方式帶來的不便,錯誤值一般作為返回值列表的最後乙個,其他返回值是成功執行時需要返回的資訊。為了避免錯誤處理時過深的**縮排:

if err !=

nilelse

if err !=

nil// normal code

雖然這種錯誤處理方式**寫起來會有一些冗長,但 golang 風格的**應該盡量使用這種方式。

下面的示例**中,當需要返回錯誤時,每次都呼叫errors.new()返回乙個 error 物件:

func

dostuff()

error

else

}

這種做法的問題是當需要對特定錯誤進行處理時,不方便對錯誤值進行等值判斷。只能用error()取出字串比較,這樣即不優雅也容易出現拼寫錯誤。最佳的做法是預定義全域性的錯誤值:

var errnospaceleft = errors.

new(

"no space left on the device"

)var errpermissiondenied = errors.

new(

"permission denied"

)func

dostuff()

error

else

}

這樣錯誤值判斷就方便多了:

if err == errnospaceleft
標準庫中也預定義了一些錯誤值 ,最常用的就是io.eof

http 表示客戶端的錯誤狀態碼有幾十個。如果為每種狀態碼都預定義相應的錯誤值,**會變得很繁瑣:

var errbadrequest = errors.

new(

"status code 400: bad request"

)var errunauthorized = errors.

new(

"status code 401: unauthorized"

)// ...

這種場景下最佳的最法是自定義一種錯誤型別,並且至少實現error()方法(滿足 error 定義):

這種方式下進行等值判斷時需要轉成具體的自定義型別然後取出 code 字段判斷:

func

request()

error

}func

main()

else

}}

自定義錯誤型別可以提供更多特定的錯誤資訊,標準庫中也有乙個這樣的例子os.patherror

golang 新手很容易把 panic 當成其他語言的異常(exception)導致 panic 的濫用。這裡解釋了為什麼 golang 沒有提供類似其他語言的異常機制的原因:主要是 try / catch 的方式處理錯誤過於複雜。使用 panic / recover 的機制也可以達到類似 try / catch 的效果,但是要特別謹慎的使用。

panic / recover 和 try / catch 機制最大的不同在於控制流程上的區別。try / catch 機制控制流作用在 try **塊內,**塊執行到異常拋出點(throw)時,控制流跳出 try **塊,轉到對應的 catch **塊,然後繼續往下執行。panic / recover 機制控制流則作用在整個 goroutine 的呼叫棧。當 goroutine 執行到 panic 時,控制流開始在當前 goroutine 的呼叫棧內向上回溯(unwind)並執行每個函式的 defer 。如果 defer 中遇到 recover 則回溯停止,如果執行到 goroutine 最頂層的 defer 還沒有 recover ,執行時就輸出呼叫棧資訊然後退出。所以如果要使用 recover 避免 panic 導致程序掛掉,recover 必須要放到 defer 裡。

recover 呼叫的位置如果不對是無法將 panic 恢復的,這裡有個比較複雜的規則,具體可以參考這裡。為了避免過於複雜的**,最好不要使用巢狀的 defer ,並且 recover 應該直接放到 defer 函式裡直接呼叫。

panic 主要用於以下場景:

發生嚴重錯誤(critical error)必須讓程序退出。這裡「嚴重」判斷的標準是錯誤無法恢復導致程式無法執行或者繼續執行會發生不可預期的行為。有點類似如 c 語言的 assert 機制。標準庫中有些函式也是使用這種機制,比如regexp.mustcompile。當傳入的引數不是乙個合法的正規表示式時,繼續執行已經沒有任何意義。另外一些場景,比如程式啟動時依賴的資料庫不存在或者依賴的配置不可讀取,這個時候如果繼續執行可能會導致發生不可預期的行為,這個時候使用 panic 讓程序直接退出將問題暴露反而是更可取的做法。非「嚴重」的錯誤比如客戶端不合法的請求引數應該返回 error 然後讓呼叫者去處理而不是使用 panic 讓程序退出。

快速退出錯誤處理。也就是上文中提到的模擬 try / catch 的行為。大多數情況下錯誤處理應該使用 error 機制,但有時候函式呼叫棧很深,逐層返回錯誤可能需要寫很多冗餘**,這個時候可以使用 panic 讓程式的控制流直接跳到頂層的 recover 處來處理錯誤。這種場景要特別注意在包內的 panic 必須在包內就要 recover 。讓 panic 跨包傳遞可能會導致更複雜的問題,所以包的匯出函式不應該產生 panic (上述 1 中的場景除外)。

本文簡要介紹了 golang 的錯誤處理與異常機制。error 是 golang 中錯誤處理的主要方式,golang 程式應該盡量使用 error 來進行錯誤處理。當需要對 error 進行等值判斷以針對特定的錯誤型別進行處理處理時,預定義全域性的錯誤值是比較優雅的方式。當需要提供更多額外的錯誤資訊時,可以自定義錯誤型別,但至少要實現error()方法以滿足 error 的定義。golang 雖然也有類似其他語言的異常的 panic ,但是使用場景很有限,如非必要,不要使用。

golang 錯誤處理

go 程式使用 error 值來表示錯誤狀態。與 fmt.stringer 類似,error 型別是乙個內建介面 type error inte ce 與 fmt.stringer 類似,fmt 包在列印值時也會滿足 error。通常函式會返回乙個 error 值,呼叫的它的 應當判斷這個錯誤是否等...

golang 錯誤處理

一 defer package main import fmt os bufio func trydefer func writefile filename string else return defer file.close 無論return panic最後都會被執行 writer bufio....

Golang的錯誤處理

1 當錯誤 panic 發生後,程式就會退出 崩潰 2 希望發生錯誤後,能夠捕獲到錯誤,並對其進行處理,保證後續程式能夠繼續執行 go中引入的處理方式是 defer panic recover。其中go中可以丟擲乙個panic異常,然後在defer中通過recover捕獲這個異常,然後正常處理。1 ...