阅读 117

Go语言中的异常处理

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步,驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。

上篇分析singleflight的相关资料中,看到有文章说“doCall方法巧妙的使用两个defer来区分调用函数异常与系统异常”。今天查找资料,学习一下Go的异常处理机制,目前的大概印象,只知道一个关键字panic

异常与错误

在Go语言中,错误被认为是一种可以预期的结果;而异常则是一种非预期的结果,发生异常可能表示程序中存在BUG或发生了其它不可控的问题。

Go 中主要通过 error 和 panic 分别表示错误和异常[2]

例如,从一个map查询一个结果时,可以通过额外的布尔值判断是否成功,属于一种预期的结果。

if v, ok := m["key"]; ok {
    return v
}
复制代码

错误

error

Go中的错误类型:error

type error interface {
    Error() string
}
复制代码

内置的 error 接口使得开发人员可以为错误添加任何所需的信息,error 可以是实现 Error() 方法的任何类型,具体例子可参考[2][5]。

Go中errors包提供了几个常用的函数,包括errors.New, errors.Is, errors.As, errors.Unwrap ,以及使用fmt.Errorf

erros.Is判断两个error是否相等,error.As判断error是否为特定类型。

使用实例

函数通常可在最后一个返回值中返回错误信息,一个简单的应用实例:

package main

import (
	"errors"
	"fmt"
)

func myF(f float64) (float64, error) {
	if f < 0 {
		return 0, errors.New("Not legal input ")
	}
	// 实现
	return 0.0, nil
}

func main() {
	var m map[string]string
	m = make(map[string]string)
	m["a"] = "2"
	_, ok := m["a"]
	_, ok2 := m["b"]
	fmt.Println(ok) // true
	fmt.Println(ok2) // false

	_, e := myF(-1)
	_, e2 := myF(2)
	fmt.Println(e) // Not legal input
	fmt.Println(e2) // <nil>
}
复制代码

异常

defer,panic recover搭配可以处理异常。

defer

当程序出现异常,如数组访问越界这类“意料之外”的错误时,它能够导致程序运行崩溃,此时就需要开发人员捕获异常并恢复程序的正常运行流程。捕获异常不是最终的目的。如果异常不可预测,直接输出异常信息是最好的处理方式[1]。

defer是Go提供的一种延迟执行机制,每次执行 defer,都会将对应的函数压入栈中。在函数返回或者 panic 异常结束时,Go 会依次从栈中取出延迟函数执行。

panic

panic用于主动抛出程序执行的异常,会终止其后将要执行的代码,并依次逆序执行 panic 所在函数可能存在的 defer 函数列表。

recover

recover 关键字主要用于捕获异常,将程序状态从严重的错误中恢复到正常状态。 必须在 defer 函数中才能生效。

下面是一个defer+panic+recover的代码样例,可以看到,在手动panic后,执行了defer中的输出,并且,a的值为0,所以如果函数中有panic语句,name函数应该需要返回一个error

package main

import "fmt"

func main() {
	for i := 0; i < 10; i++ {
		a := my(i)
		fmt.Println(a)
	}
}

func my(i int) int {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("发生了异常", err)
		}
	}()
	if i != 5 {
		return i
	} else {
		panic("panic")
	}
	return -1
}

复制代码

代码输出结果:

0
1
2
发生了异常 panic
0
4
复制代码

一个处理极端

超级健壮的代码,每个函数开始的地方都添加如下代码:

func myfunc() {
    defer func() {
       if err := recover(); err != nil {
          fmt.Println(err)
      } 
    }()
    // 函数实现....
}
复制代码

当然,不要总这么做~~~

[1] 错误和异常

[2] 没有 try-catch,该如何处理 Go 错误异常

[3] Go语言中defer的一些坑

[4] Go 语言踩坑记——panic 与 recover

[5] [译] Part 31: golang 中的自定义 error

文章分类
后端
文章标签