go語言異常處理
go語言異常處理
error介面
go語言引入了一個關於錯誤錯裡的標準模式,即error介面,該介面的定義如下:
type error interface{
Error() string
}
對於要返回錯誤的大多數函式來說,大致上都可以定義為如下的模式,將error作為多個返回值中的最後一個,但是這並非是強制要求
func Foo(param int) (n int,err error){
//
}
呼叫該函式的時候建議按照如下的方式處理錯誤情況
n,err := Foo(0) if err != nil{ //處理錯誤 }else{ //處理結果 } 那麼,如何自定義error型別呢,我們以Go庫中的實際程式碼為例來說明,首先,定義一個承載錯誤資訊的型別,因為Go中的介面非常的靈活,你根本不需要像別的語言一樣使用繼承或則implements來明確指定型別和介面之間的關係,程式碼如下:
type PathError struct{
Op string
Path string
Err error
}
這樣定義後,編譯器如何知道PathError可以當成一個error來傳遞呢?關鍵在於下面的程式碼實現了Error() 方法:
func (e *PathError) Error() string{
return e.Op + " " + e.Path +" " + e.Err.Error()
}
定義了上述方法之後,就可以直接返回PathError變量了,如下:
func test(name string) (fi FIleInfo,err error){
var stat syscall.Stat_t
err = syscall.Stat(name,&stat)
if err != nil{
return nil,$PathError{"stat",name,err}
}
return file,nil
}
如果在處理錯誤的時候要獲取錯誤的詳細資訊,就需要用到型別的轉換知識了:
fi,err := os.Stat("a.txt")
if err != nil{
if e,ok := err.(*os.PathError);ok && e.Err != nil{
//獲取PathError型別變數e總的其他資訊
}
}
defer
關鍵字defer是Go引入的一個很有意思的特性,他能保證在函式中發生異常的情況下,defer定義的語句仍然會被執行,如下:
func CopyFile(dst,src string) (w int64,err error){ srcFile,err := os.Open(src) if err != nil{ return } defer srcFile.Close() dstFile,err := os.Create(dst) if err != nil{ return } defer dstFile.Close() return io.Copy(dstFile,srcFile) }
即使Copy函式丟擲異常,Go仍然會保證檔案被正常的關閉,如果據的一句話幹不完清理工作的話,可以在defer後加一個匿名函式:
defer func(){
//
}()
值得注意的是一個函式中可以定義多個defer,並且defer語句的順序是按照先進後出的順序執行的,也就是說最後一個defer語句將最先被執行
panic()和recover()
GO引入了兩個內建的函式panic()和recover()以報告和處理執行時錯誤和程式中的錯誤場景
func panic(interface{})
func recover() interface{}
當在一個函式中呼叫panic函式後,正確的執行流程會被立即終止,但函式中之前使用defer關鍵字延遲執行的語句將正常展開執行,之後該函式將返回到呼叫函式,並導致逐步向上執行panic流程,直到所屬的goroutine中所有正在執行的函式被終止,錯誤資訊將被報告,這個過程成為錯誤處理流程
從panic中傳入的interface{}中我們可以看出,該函式接收任意型別的資料
recover用於終止錯誤處理流程,一般情況下,recover應該在一個使用defer關鍵字的函式中執行以有效擷取錯誤處理流程,如果在發生異常的goroutine中沒有呼叫recover,會導致該goroutine所屬的程序直接退出
下面是一個常用的場景
defer func(){
if r := recover();r!=nil{
log.Printf("Runtime error caught: %v",r)
}
}