go1.13errors的用法
go1.13errors的用法
前言
go 1.13发布了error的一些新的特性,那么就来探究学习下。
基本用法
fmt.Errorf
使用 fmt.Errorf 加上 %w 格式符来生成一个嵌套的 error,它并没有像 pkg/errors 那样使用一个 Wrap 函数来嵌套 error,非常简洁。
err1 := errors.New("new error")
err2 := fmt.Errorf("err2: [%w]", err1)
err3 := fmt.Errorf("err3: [%w]", err2)
fmt.Println(err3)
输出
// output
err3: [err2: [new error]]
err2 就是一个合法的被包装的 error,同样地,err3 也是一个被包装的 error,如此可以一直套下去。
Unwrap
拆开一个被包装的 error
func Unwrap(err error) error
将嵌套的 error 解析出来,多层嵌套需要调用 Unwrap 函数多次,才能获取最里层的 error。
源码如下:
func Unwrap(err error) error {
// 判断是否实现了 Unwrap 方法
u, ok := err.(interface {
Unwrap() error
})
// 如果不是,返回 nil
if !ok {
return nil
}
// 调用 Unwrap 方法返回被嵌套的 error
return u.Unwrap()
}
对 err 进行断言,看它是否实现了 Unwrap 方法,如果是,调用它的 Unwrap 方法。否则,返回 nil。
err1 := errors.New("new error")
err2 := fmt.Errorf("err2: [%w]", err1)
err3 := fmt.Errorf("err3: [%w]", err2)
fmt.Println(errors.Unwrap(err3))
fmt.Println(errors.Unwrap(errors.Unwrap(err3)))
输出
// output
err2: [new error]
new error
errors.Is
判断被包装的error是是否含有指定错误。
当多层调用返回的错误被一次次地包装起来,我们在调用链上游拿到的错误如何判断是否是底层的某个错误呢?
它递归调用 Unwrap 并判断每一层的 err 是否相等,如果有任何一层 err 和传入的目标错误相等,则返回 true。
源码如下:
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
// 无限循环,比较 err 以及嵌套的 error
for {
if isComparable && err == target {
return true
}
// 调用 error 的 Is 方法,这里可以自定义实现
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// 返回被嵌套的下一层的 error
if err = Unwrap(err); err == nil {
return false
}
}
}
通过一个无限循环,使用 Unwrap 不断地将 err 里层嵌套的 error 解开,再看被解开的 error 是否实现了 Is 方法,并且调用它的 Is 方法,当两者都返回 true 的时候,整个函数返回 true。
举个栗子
err1 := errors.New("new error")
err2 := fmt.Errorf("err2: [%w]", err1)
err3 := fmt.Errorf("err3: [%w]", err2)
fmt.Println(errors.Is(err3, err2))
fmt.Println(errors.Is(err3, err1))
输出
// output
true
true
As
这个和上面的 errors.Is 大体上是一样的,区别在于 Is 是严格判断相等,即两个 error 是否相等。而 As 则是判断类型是否相同,并提取第一个符合目标类型的错误,用来统一处理某一类错误。
func As(err error, target interface{}) bool
源码如下:
func As(err error, target interface{}) bool {
// target 不能为 nil
if target == nil {
panic("errors: target cannot be nil")
}
val := reflectlite.ValueOf(target)
typ := val.Type()
// target 必须是一个非空指针
if typ.Kind() != reflectlite.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
// 保证 target 是一个接口类型或者实现了 Error 接口
if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) {
panic("errors: *target must be interface or implement error")
}
targetType := typ.Elem()
for err != nil {
// 使用反射判断是否可被赋值,如果可以就赋值并且返回true
if reflectlite.TypeOf(err).AssignableTo(targetType) {
val.Elem().Set(reflectlite.ValueOf(err))
return true
}
// 调用 error 自定义的 As 方法,实现自己的类型断言代码
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
// 不断地 Unwrap,一层层的获取嵌套的 error
err = Unwrap(err)
}
return false
}
举个栗子
type ErrorString struct {
s string
}
func (e *ErrorString) Error() string {
return e.s
}
var targetErr *ErrorString
err := fmt.Errorf("new error:[%w]", &ErrorString{s:"target err"})
fmt.Println(errors.As(err, &targetErr))
输出
// output
true
扩展
Is As 两个方法已经预留了口子,可以由自定义的 error struct 实现并覆盖调用。
参考
【Go 1.13 errors 基本用法】https://segmentfault.com/a/1190000020398774
【Go语言(golang)新发布的1.13中的Error Wrapping深度分析】https://www.flysnow.org/2019/09/06/go1.13-error-wrapping.html
go1.13errors的用法的更多相关文章
- GoCN每日新闻(2019-10-18)
GoCN每日新闻(2019-10-18) 在Go1.13使用Errors https://blog.golang.org/go1.13-errors Go的扁平化应用结构 https://www.ca ...
- govendor用法
为什么使用govendor go语言的依赖管理最主要的是版本控制问题. govendor是Golang的依赖包管理工具,它的出现可以避免不同用户在clone同一个项目后从外部获取不同依赖库版本的问题. ...
- Docker基础用法篇
Docker基础用法篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装docker 1>.依赖的基础环境 64 bits CPU Linux Kerner 3.10+ ...
- Go package(2) strings 用法
go version go1.10.3 Go中的字符串用法,可以在 godoc.org 上查看语法和用法. 最简单的语法就是获取字符串中的子串 s := "hello world" ...
- Go1.14发布了,快来围观新的特性啦
如期而至,Go1.14发布了,和往常一样,该版本保留了Go 1兼容性的承若,这个版本的大部分更新在工具链 .运行时库的性能提升方面,总的来说,还是在已有的基础上不断优化提成,大家期待的泛型还没有到来, ...
- EditText 基本用法
title: EditText 基本用法 tags: EditText,编辑框,输入框 --- EditText介绍: EditText 在开发中也是经常用到的控件,也是一个比较必要的组件,可以说它是 ...
- jquery插件的用法之cookie 插件
一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...
- Java中的Socket的用法
Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...
- [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法
一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...
随机推荐
- 有关KMP算法
KMP算法: 此算法的本质是首先对于模板字符串进行计算,生成一个数组(next数组),该数组反映了模板字符串的情况. 例: S: ABADACABABCD P: ABAB 当我们查询到P3与S3(B和 ...
- atomic的底层实现
atomic操作 在编程过程中我们经常会使用到原子操作,这种操作即不想互斥锁那样耗时,又可以保证对变量操作的原子性,常见的原子操作有fetch_add.load.increment等. 而对于atom ...
- JS中iframe子页面与父页面之间通信
iframe子页面与父页面通信根据iframe中src属性是同域链接还是跨域链接,通信方式也不同. 一.同域下父子页面的通信 父页面parent.html <html> <head& ...
- scapy学习笔记
1.ACK Scan >>>ans,unans=sr(IP(dst="www.baidu.com")/TCP(dport=[80,666],flags=" ...
- ysoserial分析【二】7u21和URLDNS
目录 7u21 gadget链分析 hashCode绕过 参考 URLDNS 7u21 7u21中利用了TemplatesImpl来执行命令,结合动态代理.AnnotationInvocationHa ...
- sql-lib闯关11-20关
从第11关开始,我们就进入到了POST注入的世界了. POSTpost是一种数据提交方式,它主要是指数据从客户端提交到服务器端,例如,我们常常使用的用户登录模块.网站的留言板模块等,在这些功能模块中我 ...
- adb的连接设备故障分析(三)
一,如果使用adb devices进行检测,发现没有任何设备信息,我们就需要检查是否有手机/模拟器连接上 二,如果是手机进行连接,windows右下角有出来如下提示的话,需要检查你的手机驱动是否有安装 ...
- Django安装和使用
Django安装 安装Django Python3在线安装Django pip3 install Django 验证是否安装成功,并查看当前版本 python -m django --version ...
- Python学习笔记:函数详解(下)
本文介绍:高阶函数,嵌套函数,以及由前面两个组成的装饰器 一.高阶函数:以下两种情况都是高阶函数 1.将函数名当成参数传递给另外一个函数(作用:不修改被传递函数源代码就可以添加新功能): impo ...
- CentOS-7.6 下搭建 NIS 服务器
##服务端配置: ####Server: 192.168.0.178(CentOS 7.6) # systemctl stop firewalld # systemctl disable firewa ...