其他语言,如python,使用try/catch语句来捕捉错误,抛出异常,Go中是没有的。
Go实现捕捉错误的方法是defer-panic-recover机制。
通过在函数中返回错误码,告知用户收到错误。错误码为nil代表没有发生错误,否则就是出错了,需要程序员自定义收到此错误后,执行什么样的操作?

  1. j, err := json.Marshal(s) //返回[]byte和err两个返回值
  2. if err != nil {
  3. fmt.Println("序列化失败")
  4. return
  5. }

按规范来说,错误是必须要处理的,如果非要不处理也可以,使用下划线空白符变量接收。

13.1 错误处理

13.1.1 自定义错误

如果是自己写的函数,需要返回错误码,那么错误码就要自己来定义,模拟内置的错误处理。
定义一个error类型的变量,当函数出错,则返回此变量,否则返回nil。

  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. )
  6. var e error = errors.New("无效数字或小于0") //自定义一个错误变量,是error类型的,error是一个接口
  7. func test(f int) error {
  8. if f < 0 {
  9. return e
  10. }
  11. return nil
  12. }
  13. func main() {
  14. if err := test(-1); err != nil {
  15. fmt.Println(err) //无效数字或小于0
  16. }
  17. err := test(1)
  18. fmt.Println(err) //<nil>
  19. }

13.1.2 格式化错误处理

在上面的test判断小于0的例子中,如果想要输出更多的错误信息,比如输出小于0的具体值,那么就要连f也输出,让用户知道,因为输入了什么值导致的错误。
使用fmt包的Errorf函数。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func test(f int) error {
  6. if f < 0 {
  7. return fmt.Errorf("无效数字或小于0,输入的是:%v", f)
  8. }
  9. return nil
  10. }
  11. func main() {
  12. if err := test(-1); err != nil {
  13. fmt.Println(err) //无效数字或小于0,输入的是:-1
  14. }
  15. err := test(1)
  16. fmt.Println(err) //<nil>
  17. }

13.2 运行时异常和panic

13.2.1 panic

当发生像数组下标越界,或类型断言失败这样的运行错误时,Go就会触发运行时panic,伴随着程序的崩溃,抛出一个runtime.Error接口类型的值。
panic()可以直接使用,当程序执行到panic()的时候,就会产生一个终止程序的运行时错误。
panic()可以接受任意类型的参数,但通常是字符串,用于打印错误信息。

  1. package main
  2. func main() {
  3. panic("发生了致命错误")
  4. /*panic: 发生了致命错误
  5. goroutine 1 [running]:
  6. main.main()
  7. F:/Codes/Go/test/main.go:4 +0x27
  8. exit status 2
  9. */
  10. }

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的一个场景例子:

  1. // panic_recover.go
  2. package main
  3. import (
  4. "fmt"
  5. )
  6. func badCall() {
  7. panic("发生了panic") //panic,结束该函数调用
  8. }
  9. func test() {
  10. defer func() {
  11. if e := recover(); e != nil {
  12. fmt.Printf("挽救不了, %s\r\n", e)
  13. }
  14. }()
  15. badCall()
  16. fmt.Printf("badcall之后执行\r\n") // 这条不会执行,因为defer执行了
  17. }
  18. func main() {
  19. fmt.Printf("调用 test\r\n")
  20. test()
  21. fmt.Printf("调用test结束\r\n")
  22. }
  23. /*
  24. 调用 test
  25. 挽救不了, 发生了panic
  26. 调用test结束
  27. */