golang 函數四 (錯誤處理)

爲了方便實現自定義錯誤類型,Go語言標準庫中將error定義爲接口類型。比如:

type error interface{
    Error() string
}

按照Go語言編程習慣,error總是最後一個函數返回值,並且標準庫提供了創建函數,可以方便的創建錯誤消息的error對象。比如:

func divTest(x ,y int)(int, error){
    if y == 0{
        return 0, errors.New("division by zero") //創建錯誤消息的error對象
    }   
    return x/y,nil
}
func main(){
    v, err := divTest(3,0)
    if err != nil{
        log.Fatalln(err.Error())
    }   
    println(v)
}

日常開發中,我們需要根據需求自定義錯誤類型,可以存放更多的上下文信息,或者根據錯誤類型做出相應的錯誤處理。比如:

type NegativeError struct{
    x, y int
}
func (NegativeError)Error()string{
    return "negative value error"
}

type MolError struct {
    x, y int
}
func (MolError)Error()string{
    return "devision by zero"
}

func molTest(x ,y int)(int, error){
    if y == 0{
        return 0, MolError{x,y}
    }
    if x < 0 || y < 0{
        return 0, NegativeError{x,y}
    }
    return x%y,nil
}

func main(){
    v, err := molTest(3,-1)
    if err != nil{
        switch e := err.(type){     //獲取錯誤類型
            case MolError:
                println(e.x,e.y)
            case NegativeError:
                println(e.x,e.y)
            default:
                println(e)
        }
        log.Fatalln(err.Error())
    }
    println(v)
}

與error相比,panic/recover 在應用上更類似於 try/catch 結構化。比如:

func panic() interface{}   
func recover() interface{}

兩者區別:panic 立即中斷當前函數處理流程,執行延遲調用。recover在延遲調用中可以捕獲並返回panic產生的錯誤對象,比如:

func Myrecover(){
    if err := recover(); err != nil{
        log.Fatalln(err)
    }   
}

func main(){
    println("start...")
    defer Myrecover()
    panic("dead")
    println("end...")
}
輸出:
start...
2017/02/09 11:24:13 dead
exit status 1

如果有連續多次調用panic的場景,只有最後一次panic會被recover捕獲處理,比如:

func Myrecover(){
    if err := recover(); err != nil{
        log.Fatalln(err)
    }   
}

func main(){
    defer Myrecover()
    defer func(){
        panic("a bad problem")
    }()
    panic("a problem")
}
輸出:
2017/02/09 11:31:50 a bad problem
exit status 1

recover只有在延遲調用函數中才能得到正常工作,比如:

func main() {
    defer Myrecover()
    defer log.Println(recover())
    defer println(recover())
    panic("a problem") 
}
輸出:
(0x0,0x0)
2016/11/12 07:07:54 <nil>
2016/11/12 07:07:54 a problem
exit status 1

在日常開發過程中,經常需要進行調試,可以使用函數輸出完整的調用棧信息,比如:

func Myrecover(){
    if err := recover(); err != nil{
        fmt.Println(err)
        debug.PrintStack()
        //log.Fatalln(err)
    }
}
func main(){
    defer Myrecover()
    panic("a problem")
}
輸出:
a problem
goroutine 1 [running]:
runtime/debug.Stack(0xc42002c010, 0xc42003fe20, 0x1)
	/root/data/go/src/runtime/debug/stack.go:24 +0x79
runtime/debug.PrintStack()
	/root/data/go/src/runtime/debug/stack.go:16 +0x22
main.Myrecover()
	/root/data/gopath/test/panic.go:10 +0x85
panic(0x48a5e0, 0xc42000a320)
	/root/data/go/src/runtime/panic.go:458 +0x243
main.main()
	/root/data/gopath/test/panic.go:16 +0x8d

日常開發中,只有在系統發生了不可恢復性或無法正常工作的錯誤可以使用panic,比如端口號被佔用、數據庫未啓動、文件系統錯誤等,否則不建議使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章