Go 1.13 之后的 error 检查

Go 1.13 之后的 error 检查

起步

如果说 Go 有很多诟病的地方,那么 Go 中 error 的处理一定可以挤进吐槽榜单前十。既然 try语句提议被一拒再拒,我们也只好用着古老的 if 筛选错误。Go 官方并非没有意识到 error 的鸡肋问题,于是在 Go 1.13 提出了新解决方案,总的说来就是“三个 api + 一个格式占位符”。

error 从何而来

在 Go 中,error 从何而来呢?熟练使用 Go 的人一定知道以下几种方式:

  • errors.New
  • fmt.Errorf
  • 直接返回函数调用后得到的 error
  • 定义一个结构体,实现 error 接口(type error Interface { Error() string }

以“打开一个文件”为例,按照上面的处理方式代码可以分别是:

// errors.New
func openConfigFile(path string) error {
   
	_, err := os.Open(path)
	if err != nil {
   
	    // 返回新的 error 实例
		return errors.New("can not open the specific file")
	}
	return nil
}
// fmt.Errorf
func openConfigFile(path string) error {
   
	_, err := os.Open(path)
	if err != nil {
   
	    // 返回新的 error 实例,同时包含了实际 error
		return fmt.Errorf("can not open the specific file, reason: %v", err)
	}
	return nil
}
// return error that called function returned
func openConfigFile(path string) error {
   
	_, err := os.Open(path)
	if err != nil {
   
	    // 直接返回得到的 error,不做任何处理
		return err
	}
	return nil
}
// 自定义 error
type OpenErr struct {
   
	Err error  // 存放原始 error
}

// 实现 error 接口
func (*OpenErr) Error() string {
   
	return "can not open the specific file"
}

func openConfigFile(path string) error {
   
	_, err := os.Open(path)
	if err != nil {
   
		return &OpenErr{
   Err:err}
	}
	return nil
}

上述四种方法各有千秋。如方法一,errors.New 会返回一个新的 error,其存放的数据就是我们传入的 text(“can not open the specific file”)。采用这种方式通常是为了告诉调用者出错了,但实际的错误细节不愿暴露。对调用者来说,他可能不太关心出了什么错,只在乎有没有出错。

方法二跟方法一相同,也会隐藏原始错误(这里指错误类型),但通常会将原始错误的字符串说明一起返回。在该处理方式中,“can not open the specific file” 为额外提示语,“reason: %v” 显示错误细节。一般来讲,调用 fmt.Errorf 更大几率是要产生一个给人看的错误,而不是让代码去解析(尽管并非不能)。

方法三,通常是函数调用者需要根据 error 的实际类型,或实际值,确定下一步的执行策略。也就是说它需要解析 error,所以函数返回“原味” error。这样做的缺点是,没办法对 error 添加额外信息。

方法四可以认为是上述三种方法的集合,既可以保留原始 error,还可以添加额外信息。通过这些元数据随意组合,调用者想要的样子它都有。缺点就是代码要多写一些。

如何检查 error

现在 error 有了,我们应该如何检查错误呢?在 1.13 之前,常见有:1. 比较值;2. 比较类型。

拿官方源码举例更具说服力。我们先来看看比较值。

func (db *DB) QueryContext(ctx context.Context, 
                           query string, 
                           args ...interface{
   }) (*Rows, error) {
   
	var rows *Rows
	var err error
	for i := 0; i < maxBadConnRetries; i++ {
   
		rows, err = db.query(ctx, query, args, cachedOrNewConn)
		if err != driver.ErrBadConn {
     // 比较值
			break
		}
	}
	if err == driver.ErrBadConn {
     // 比较值
		return db.query(ctx, query, args, alwaysNewConn)
	}
	return rows, err
}

func (db *DB) Query(query string, 
                    args ...interface{
   }) (*Rows, error) {
   </
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值