其他语言,如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 mainimport ("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 mainimport ("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 mainfunc main() {panic("发生了致命错误")/*panic: 发生了致命错误goroutine 1 [running]:main.main()F:/Codes/Go/test/main.go:4 +0x27exit 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.gopackage mainimport ("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结束*/
