其他语言,如python,使用try/catch语句来捕捉错误,抛出异常,Go中是没有的。
Go实现捕捉错误的方法是defer-panic-recover机制。
通过在函数中返回错误码,告知用户收到错误。错误码为nil代表没有发生错误,否则就是出错了,需要程序员自定义收到此错误后,执行什么样的操作?
j, err := json.Marshal(s) //返回[]byte和err两个返回值
if err != nil {
fmt.Println("序列化失败")
return
}
按规范来说,错误是必须要处理的,如果非要不处理也可以,使用下划线空白符变量接收。
13.1 错误处理
13.1.1 自定义错误
如果是自己写的函数,需要返回错误码,那么错误码就要自己来定义,模拟内置的错误处理。
定义一个error类型的变量,当函数出错,则返回此变量,否则返回nil。
package main
import (
"errors"
"fmt"
)
var e error = errors.New("无效数字或小于0") //自定义一个错误变量,是error类型的,error是一个接口
func test(f int) error {
if f < 0 {
return e
}
return nil
}
func main() {
if err := test(-1); err != nil {
fmt.Println(err) //无效数字或小于0
}
err := test(1)
fmt.Println(err) //<nil>
}
13.1.2 格式化错误处理
在上面的test判断小于0的例子中,如果想要输出更多的错误信息,比如输出小于0的具体值,那么就要连f也输出,让用户知道,因为输入了什么值导致的错误。
使用fmt包的Errorf函数。
package main
import (
"fmt"
)
func test(f int) error {
if f < 0 {
return fmt.Errorf("无效数字或小于0,输入的是:%v", f)
}
return nil
}
func main() {
if err := test(-1); err != nil {
fmt.Println(err) //无效数字或小于0,输入的是:-1
}
err := test(1)
fmt.Println(err) //<nil>
}
13.2 运行时异常和panic
13.2.1 panic
当发生像数组下标越界,或类型断言失败这样的运行错误时,Go就会触发运行时panic,伴随着程序的崩溃,抛出一个runtime.Error接口类型的值。
panic()可以直接使用,当程序执行到panic()的时候,就会产生一个终止程序的运行时错误。
panic()可以接受任意类型的参数,但通常是字符串,用于打印错误信息。
package main
func main() {
panic("发生了致命错误")
/*panic: 发生了致命错误
goroutine 1 [running]:
main.main()
F:/Codes/Go/test/main.go:4 +0x27
exit status 2
*/
}
13.2.2 Go panicking
在多层嵌套函数调用中,调用panic,可以马上终止当前函数的执行,所有的defer语句都会保证执行,并把控制权交还给接收到panic的函数调用者。这样向上冒泡,并执行每层的defer,在栈顶程序崩溃,并在命令行用传给panic的值,报告错误情况,这个终止过程就是panicking。
标准库中,有很多包含Must前缀的函数,这些函数容易panic。
13.2.3 recover
recover的作用是当发生panic的时候,尝试恢复,尝试挽救程序,不让其崩溃,继续执行,让程序从panicking中重获控制权。
recover只在defer使用,下面是panic+defer+recover的一个场景例子:
// panic_recover.go
package main
import (
"fmt"
)
func badCall() {
panic("发生了panic") //panic,结束该函数调用
}
func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("挽救不了, %s\r\n", e)
}
}()
badCall()
fmt.Printf("badcall之后执行\r\n") // 这条不会执行,因为defer执行了
}
func main() {
fmt.Printf("调用 test\r\n")
test()
fmt.Printf("调用test结束\r\n")
}
/*
调用 test
挽救不了, 发生了panic
调用test结束
*/