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

go語言 異常處理

技術標籤:gogolanggo

異常處理

Go語言追求簡潔優雅,所以,Go語言不支援傳統的 try…catch…finally 這種異常,因為Go語言的設計者們認為,將異常與控制結構混在一起會很容易使得程式碼變得混亂。因為開發者很容易濫用異常,甚至一個小小的錯誤都丟擲一個異常。在Go語言中,使用多值返回來返回錯誤。不要用異常代替錯誤,更不要用來控制流程。在極個別的情況下,也就是說,遇到真正的異常的情況下(比如除數為0了)。才使用Go中引入的Exception處理:defer, panic, recover。

1 error

Go語言內建了一個簡單的錯誤介面作為一種錯誤處理機制,介面定義如下:

type error interface {
	Error() string
}
它包含一個 Error() 方法,返回值為string

Go的error構造有兩種方式,分別是

第一種:errors.New()

err := errors.New("This is an error")
if err != nil {
  fmt.Print(err)
}
第二種:fmt.Errorf()

err := fmt.Errorf("This is an error")
if err != nil {
  fmt.Print(err)
}
除了直接使用Go自帶的方法,還可以自定義錯誤。下面以自然數函式作為例子:

type
NotNature float64 func (err NotNature) Error() string { return fmt.Sprintf("自然數為大於或等於0的數: %v", float64(err)) } func Nature(x float64) (float64,error) { if x<0 { return 0,NotNature(x) } else { return x,nil } } func main() { fmt.Println(Nature(1)) fmt.Println(Nature(-1)) }

需要注意一下幾點:

1.如果函式需要處理異常,通常將error作為多值返回的最後一個值,返回的error值為nil則表示無異常,非nil則是有異常。

2.一般先用if語句處理error!=nil,正常邏輯放if後面。

Go語言的error代表的並不是真“異常”,只是通過返回error來表示錯誤資訊,換句話說,不是執行時錯誤範圍預定義的錯誤,某種不符合期望的行為並不會導致程式無法執行(自然數函式例子),都應使用error進行異常處理。當程式出現重大錯誤,如陣列越界,才會將其當成真正的異常,並用panic來處理。

2 panic

Go不使用try…catch方法來處理異常,而是使用panic和recover

先上程式碼舉一個簡單的例子

func main() {
  fmt.Println("Hello,Go!")
  panic(errors.New(" i am a error"))
  fmt.Println("hello,again!")
}

輸出:

Hello,Go!
panic:  i am a error

goroutine 1 [running]:
main.main()
	~/error.go:12 +0xb5
exit status 2

可以看到,panic後面的程式不會被執行了。但是我們捕捉異常並不是為了停止程式(一般情況),而是為了讓程式能正常執行下去,這時候就到recover出場了。

package main

import "fmt"

func main(){
  defer func(){
    fmt.Println("我是defer裡面第一個列印函式")
    if err:=recover();err!=nil{
        fmt.Println(err)
    }
    fmt.Println("我是defer裡面第二個列印函式")
  }()
  f()
}

func f(){
  fmt.Println("1")
  panic("我是panic")
  fmt.Println("2")
}
輸出:

1
我是defer裡面第一個列印函式
我是panic
我是defer裡面第二個列印函式
可以看到,f函式一開始正常列印,當遇到panic,就跳到defer函式,執行defer函式裡的內容

需要注意的是,defer函式裡列印的err其實就是panic裡面的內容。

下面詳細介紹一下panic和recover的原理。首先來看一下panic和recover的官方定義

func panic(v interface{})
內建函式panic會停止當前goroutine的正常執行。當函式F呼叫panic時,F的正常執行立即停止。任何被F延遲執行的函式都將以正常的方式執行,然後F返回其呼叫者。對呼叫方G來說,對F的呼叫就像呼叫panic一樣,終止G的執行並執行任何延遲的函式。直到執行goroutine中的所有函式都按逆序停止。此時,程式將以非0退出程式碼終止。此終止序列稱為panicking,可由內建函式recover控制。
func recover() interface{}
recover內建函式允許程式管理panicking的goroutine的行為。在defer函式(但不是它呼叫的任何函式)內執行恢復呼叫,通過恢復正常執行來停止panicking序列,並檢索傳遞給panic呼叫的錯誤值。如果在defer函式之外呼叫recover,則不會停止panicking的序列。在這種情況下,或者當goroutine不panicking時,或者提供給panic的引數是nil,recover返回nil。因此,recover的返回值報告goroutine是否panicking
ps:defer和recover必須在panic之前定義,否則無效。

3 原始碼分析

errors.New的定義如下

// src/errors/errors.go

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

1.New函式返回格式為給定文字的錯誤

2.即使文字是相同的,每次對New的呼叫都會返回一個不同的錯誤值。

使用

1 defer

defer是go語言中的關鍵字,延遲指定函式的執行。通常在資源釋放、連線關閉、函式結束時呼叫。多個defer為堆疊結構,先進後出,也就是先進的後執行。defer可用於異常丟擲後的處理。

2 panic

panic是go語言中的內建函式,丟擲異常(類似java中的throw)。其函式定義為:

func panic(v interface{})
3 recover

recover() 是go語言中的內建函式,獲取異常(類似java中的catch),多次呼叫時,只有第一次能獲取值。其函式定義為:

func recover() interface{}