go語言20小時從入門到精通(九、異常處理)
##9.1 error介面 Go語言引入了一個關於錯誤處理的標準模式,即error介面,它是Go語言內建的介面型別,該介面的定義如下:
type error interface {
Error() string
}
複製程式碼
Go語言的標準庫程式碼包errors為使用者提供如下方法:
package errors
type errorString struct {
text string
}
func New(text string) error {
return &errorString{text}
}
func (e *errorString) Error() string {
return e.text
}
複製程式碼
另一個可以生成error型別值的方法是呼叫fmt包中的Errorf函式:
package fmt
import "errors"
func Errorf(format string,args ...interface{}) error {
return errors.New(Sprintf(format,args...))
}
複製程式碼
示例程式碼:
import (
"errors"
"fmt"
)
func main() {
var err1 error = errors.New("a normal err1")
fmt.Println(err1) //a normal err1
var err2 error = fmt.Errorf("%s" ,"a normal err2")
fmt.Println(err2) //a normal err2
}
複製程式碼
函式通常在最後的返回值中返回錯誤資訊:
import (
"errors"
"fmt"
)
func Divide(a,b float64) (result float64,err error) {
if b == 0 {
result = 0.0
err = errors.New("runtime error: divide by zero")
return
}
result = a / b
err = nil
return
}
func main() {
r,err := Divide(10.0,0)
if err != nil {
fmt.Println(err) //錯誤處理 runtime error: divide by zero
} else {
fmt.Println(r) // 使用返回值
}
}
複製程式碼
##9.2 panic 在通常情況下,向程式使用方報告錯誤狀態的方式可以是返回一個額外的error型別值。
但是,當遇到不可恢復的錯誤狀態的時候,如陣列訪問越界、空指標引用等,這些執行時錯誤會引起painc異常。這時,上述錯誤處理方式顯然就不適合了。反過來講,在一般情況下,我們不應通過呼叫panic函式來報告普通的錯誤,而應該只把它作為報告致命錯誤的一種方式。當某些不應該發生的場景發生時,我們就應該呼叫panic。
一般而言,當panic異常發生時,程式會中斷執行,並立即執行在該goroutine(可以先理解成執行緒,在中被延遲的函式(defer 機制)。隨後,程式崩潰並輸出日誌資訊。日誌資訊包括panic value和函式呼叫的堆疊跟蹤資訊。
不是所有的panic異常都來自執行時,直接呼叫內建的panic函式也會引發panic異常;panic函式接受任何值作為引數。 func panic(v interface{})
呼叫panic函式引發的panic異常:
func TestA() {
fmt.Println("func TestA()")
}
func TestB() {
panic("func TestB(): panic")
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
TestB()//TestB()發生異常,中斷程式
TestC()
}
複製程式碼
執行結果:
內建的panic函式引發的panic異常:
func TestA() {
fmt.Println("func TestA()")
}
func TestB(x int) {
var a [10]int
a[x] = 222 //x值為11時,陣列越界
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
TestB(11)//TestB()發生異常,中斷程式
TestC()
}
複製程式碼
執行結果:
##9.3 recover 執行時panic異常一旦被引發就會導致程式崩潰。這當然不是我們願意看到的,因為誰也不能保證程式不會發生任何執行時錯誤。
不過,Go語言為我們提供了專用於“攔截”執行時panic的內建函式——recover。它可以是當前的程式從執行時panic的狀態中恢復並重新獲得流程控制權。 func recover() interface{}
注意:recover只有在defer呼叫的函式中有效。
如果呼叫了內建函式recover,並且定義該defer語句的函式發生了panic異常,recover會使程式從panic中恢復,並返回panic value。導致panic異常的函式不會繼續執行,但能正常返回。在未發生panic時呼叫recover,recover會返回nil。
示例程式碼:
func TestA() {
fmt.Println("func TestA()")
}
func TestB() (err error) {
defer func() { //在發生異常時,設定恢復
if x := recover(); x != nil {
//panic value被附加到錯誤資訊中;
//並用err變數接收錯誤資訊,返回給呼叫者。
err = fmt.Errorf("internal error: %v",x)
}
}()
panic("func TestB(): panic")
}
func TestC() {
fmt.Println("func TestC()")
}
func main() {
TestA()
err := TestB()
fmt.Println(err)
TestC()
/*
執行結果:
func TestA()
internal error: func TestB(): panic
func TestC()
*/
}
複製程式碼
延遲呼叫中引發的錯誤,可被後續延遲呼叫捕獲,但僅最後⼀個錯誤可被捕獲:
func test() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic("defer panic")
}()
panic("test panic")
}
func main() {
test()
//執行結果:defer panic
}
複製程式碼