go-异常处理-error-panic-recover

 

Go语言的函数可以一次返回多个结果。这就为我们温和地报告错误提供了语言级别的支持。

 

func readFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return ioutil.ReadAll(file)
}
函数readFile有两个结果声明。第二个结果声明的类型是error。
error是Go语言内置的一个接口类型。它的声明是这样的:
type error interface { 
    Error() string
}

 

 

errors.New是一个很常用的函数。在Go语言标准库的代码包中有很多由此函数创建出来的错误值。
if path == "" {
    return nil, errors.New("The parameter is invalid!")
}   

 

 

 

panic

panic可被意译为运行时恐慌。因为它只有在程序运行的时候才会被“抛出来”。
内建函数panic可以让我们人为地产生一个运行时恐慌。
不过,这种致命错误是可以被恢复的,在Go语言中,内建函数recover就可以做到这一点。

 

内建函数panic和recover是天生的一对。
前者用于产生运行时恐慌,而后者用于“恢复”它。
不过要注意,recover函数必须要在defer语句中调用才有效。
因为一旦有运行时恐慌发生,当前函数以及在调用栈上的所有代码都是失去对流程的控制权。 只有defer语句携带的函数中的代码才可能在运行时恐慌迅速向调用栈上层蔓延时“拦截到”它。

 

defer func() {
    if p := recover(); p != nil {
        fmt.Printf("Fatal error: %s\n", p)
    }
}()
我们调用了recover函数。该函数会返回一个interface{}类型的值。
interface{}代表空接口。
Go语言中的任何类型都是它的实现类型。
把这个值赋给了变量p。如果p不为nil,那么就说明当前确有运行时恐慌发生。
这时我们需根据情况做相应处理。
注意,一旦defer语句中的recover函数调用被执行了,运行时恐慌就会被恢复,不论我们是否进行了后续处理。
所以,我们一定不要只“拦截”不处理。

 

反观panic函数。
该函数可接受一个interface{}类型的值作为其参数。
也就是说,我们可以在调用panic函数的时候可以传入任何类型的值。
不过,我建议大家在这里只传入error类型的值。
这样它表达的语义才是精确的。
更重要的是,当我们调用recover函数来“恢复”由于调用panic函数而引发的运行时恐慌的时候, 得到的值正是调用后者时传给它的那个参数。 因此,有这样一个约定是很有必要的。

 

运行时恐慌代表程序运行过程中的致命错误。我们只应该在必要的时候引发它。
人为引发运行时恐慌的方式是调用panic函数。
recover函数是我们常会用到的。 因为在通常情况下,我们肯定不想因为运行时恐慌的意外发生而使程序崩溃。
最后,在“恢复”运行时恐慌的时候,大家一定要注意处理措施的得当。

 

package main

import (
    "fmt"
)

func testa(num int) {
    defer func() {
        //返回错误信息
        if err := recover(); err != nil {// 
            fmt.Println(err)
        }
    }()

    var a [10]int
    a[num] = 1
    fmt.Println("normal")
}

func main() {
    testa(25)
}

 

posted @ 2019-02-19 16:56  osbreak  阅读(291)  评论(0编辑  收藏  举报