Go异常处理
一、error接口
Go 语言通过内置的错误接口提供了非常简单的错误处理机制
error类型是一个接口,其定义如下:
type error interface {
Error() string
}
我们可以在代码中通过实现error接口来生成错误信息。
任意结构体,只要实现了 Error 方法,就可以认为是 error 错误类型。
如,errors.New方法返回的就是一个实现了error接口的结构体实例,因为它是error类型:
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 }
Go提供了两种创建error的方法:
①:errors.New
②:fmt.Errorf
不过函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
import ( "errors" "fmt" ) func Hello(name string) (string, error) { // If no name was given, return an error with a message. if name == "" { // return "", fmt.Errorf("empty name") return "", errors.New("empty name") } // If a name was received, return a value that embeds the name message := fmt.Sprintf("Hi, %v. Welcome!", name) return message, nil }
我们会约定最后返回值为 error 类型,一般常见于第二个返回值,这是一个约定俗成的习惯。
因此在调用返回error的方法的时候要注意判断error是否为nil,如果不为nil,说明异常,要做异常处理:
import ( "fmt" "log" ) func main() { message, err := Hello("") // If an error was returned, print it to the console and // exit the program. if err != nil { log.Fatal(err) } // If no error was returned, print the returned message // to the console. fmt.Println(message) }
二、panic
panic是一个Go内置函数,它用来停止当前常规控制流并启动panicking(运行时恐慌)过程。
panic的触发有两种:
①:在运行时遇到错误触发 panic,比如越界访问数组,不相同类型的变量强制类型转换等
②:通过显式直接调用 panic 函数触发 panic
func panic(v interface{}) panic panic函数接收一个interface{}空接口类型的参数,也就是说,panic函数可以接收一个任意类型的参数。
func test() { // 显式调用panic panic("panic error!") }
怎么处理panic呢?
异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover() 捕获这个异常,将 panic 错误写入日志文件,将程序恢复正常执行
调用recover() 函数可以捕获panic,即通过recover() 获取panic。
但是recover() 函数仅在defer函数内部使用。所以想要使用recover捕获panic,我们需要结合defer一起使用。而且,程序在触发panic异常后,不会继续往下执行代码,只能执行defer调用的函数。
func a() int{ i:=0 return 10/i } func main() { // 捕获处理panic defer func(){ if r := recover(); r != nil { fmt.Printf("panic recover:%s", r) } }() // 触发panic a() // 不会再执行 fmt.Println("123") }
recover()是Go语言一个内置函数,可以重新获取对一个运行时恐慌的 goroutine 的控制。recover 仅在 defer 延迟函数内部使用。在正常执行程序中,调用 recover 函数,将返回 nil。如果当前 goroutine 处于恐慌状态,调用 recover 会捕获提供给 panic 的值并恢复正常执行。
通常,我们不会去捕获运行时 panic,发生 panic 异常,直接让程序崩溃即可,及时根据 panic 提供的信息,修复异常。但是,一些情况下,我们还是需要捕获 panic,比如在程序发生 panic 异常时,释放资源。比如关闭文件或者释放锁
注意:
在Go语言中,recover只在defer调用的函数中有效,并且defer要在panic之前先注册,否则不能捕获异常。当panic被捕获到后,被注册的函数将获得程序控制权
Go实现类似 try catch 的异常处理:
/* 实现类似try catch处理,后续代码可正常执行 fun() 可能触发panic的函数 hander() panic处理函数 */ func Try(fun func(), handler func(interface{})) { defer func() { if err := recover(); err != nil { handler(err) } }() fun() } func main() { Try(func() { panic("test panic") }, func(err interface{}) { fmt.Println(err) }) // 后续代码可继续执行 fmt.Println("panic handled") }
error和panic区别:
error一般是程序员可预知的,会进行合适的处理,例如检测输入是否合法等
而panic是程序员无法预知的异常,例如空指针或数组越界等
总结:panic 导致的后果非常严重,会导致程序崩溃,所以我们在处理一些不会影响程序正确运行的错误时,尽量使用 error 处理错误
三、throw
这个错误类型,在用户侧是没法主动调用的,均为 Go 底层自行调用的,像是大家常见的 map 并发读写,就是由此触发
END.