前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言中常见100问题-#49 wrap error

Go语言中常见100问题-#49 wrap error

作者头像
数据小冰
发布2022-08-15 15:19:18
3800
发布2022-08-15 15:19:18
举报
文章被收录于专栏:数据小冰

‍‍‍‍‍‍通常来说,有以下两种场景需要对error进行包装.

  • 向error中添加上下文信息
  • 将error转为一个特定的error

作者对上面的场景各举了一个例子进行说明。对需要向error中添加上下文信息的情况,以数据库操作为例,某个角色身份的人请求数据库操作,但是它没有查询权限,当它在查询的时候会返回一个没有访问权限的error. 为了方便debug问题,通过log记录错误信息,最好是记录上下文信息。本例中,我们可以通过wrap error记录是谁在访问什么资源的信息。

另一个例子是将error转为一个特定的error,作者以实现HTTP handler为例,该函数需要对调用的函数进行返回值进行检查,如果是对资源没有访问权限的error,将其包装为Forbidden类型的error,然后可以返回403状态码。这种情况下,可以通过wrap error操作将原error包装到Forbidden中。

上面的两个例子中,原始error是能够继续访问到的。调用者caller可以通过unwrap操作就能获取到原始错误error信息。需要注意的是,有时候我们可以将上面两个例子中的方法结合起来使用,即添加上下文信息又将它转换为一个特定的错误。

前面介绍了wrap error使用的两种场景,下面分析了error返回时的4种处理方‍‍‍‍‍法。分别是「直接返回、通过自定义error包装返回、通过%w和%v返回」。以下面的代码为例

代码语言:javascript
复制
func Foo() error {
       err := bar()
       if err != nil {
               // ?
       }
       // ...
}

上面代码中?的地方,有哪些处理方法?

第一种是直接返回,不做任何处理. 这种方法适合没有有用的上下文信息需要添加,也不需要新产生一个error的情况。

代码语言:javascript
复制
if err != nil {
        return err
}

第二种是自定义一个error类型返回。在Go1.13之前,wrap error需要先定义一个结构体,实现Error() string方法。这种通过自定义类型的优点是非常灵活,可以根据需要添加任何额外的信息。缺点是想重复这个操作比较麻烦,必须创建一个特定的错误类型。

代码语言:javascript
复制
type BarError struct {
        Err error
}

func (b BarError) Error() string {
        return "bar failed:" + b.Err.Error()
}

将原error包装放入BarError中的Err字段中, 代码如下。

代码语言:javascript
复制
if err != nil {
        return BarError{Err: err}
}

‍‍

第三种方法是使用直接%w, 这个是在Go1.13引入的,性质同第二种方法,优点是不用定义一个结构体实现Error方法,并且原始的错误任然是可以访问的。使用者可以通过unwrap error拿到原始的error,然后将原始的error与某种具体的类型或value进行比较。

代码语言:javascript
复制
if err != nil {
        return fmt.Errorf("bar failed: %w", err)
}

第四种方法是使用%v,示例代码如下. 与第三种方法的区别在于,使用%v返回的error不是对原error的wrap操作,而是将其转成了另一个error, 原error无法在访问了, 即调用方不能unwrap当前的error将它与原始的bar error进行比较。虽然原error不能访问,但是原error描述的信息是可以获取的。

代码语言:javascript
复制
if err != nil {
        return fmt.Errorf("bar failed: %v", err)
}

‍‍

‍‍‍‍‍

「因此,%v比%w限制更多,那是不是说在%w发布以后,我们全部使用%w就更好?」 并不完全是这样的。wrap error意味着调用者可以访问原error,这意味着调用方和被调用方存在潜在的耦合。上面的例子中,假如使用wrap error操作,调用Foo函数检查原error是否是bar error,现在实现有调整,使用其他的函数返回了另一种类型的错误,这将可能会破坏调用方。

「如果我们想让调用方不要依赖于被调用函数的具体实现细节,不应该将error 进行wrap返回,而是直接转换返回,这种情况下,使用%v而不是%w」

下面是上面四种方法的归纳总结,wrap error使用在需要添加上下文信息和将error作为一个具体类型这两种场景中。如果需要标记一个错误,我们需要自定义一个错误类型。如果我们仅仅是添加上下文信息,应该使用fmt.Errorf+%w。如果我们不想让调用者和被调用者存在耦合,不应该使用wrap error而应该直接使用fmt.Errorf+%v.

option

extra context

marking an error(标记一个错误)

source error available

直接返回error

不可以

不可以

可以

自定义错误类型

可以,如果结构体中包含有承载额外信息的字段

可以

可以,如果结构体中含有承载原error的字段,并且是可以导出的,或者提供有原error访问方法

fmt.Errorf+%w

可以

不可以

可以

fmt.Errorf+%v

可以

不可以

不可以

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小冰 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档