1. 程式人生 > >go語言異常處理

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)
    }
}