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 ...
随机推荐
- js 验证输入框是否为空
很多时候,菜鸟在对输入框是否输入值进行验证时,总会把输入“空字符串”这一状况给忽略掉.嗯,我就这样,所以记下,以后回忆下还是不错滴 <input type="text" id ...
- 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[上篇]
<200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有了真实的了解.在过去很长一段时间中,有很多人私信给我:能否按照相同的方式分 ...
- Java字符串转List
List<String> result = Arrays.asList(str.split(","));
- Journal of Proteomics Research | 构建用于鉴定蓖麻毒素的串联质谱库
文章题目:Constructing a Tandem Mass Spectral Library for Forensic Ricin Identification 构建用于鉴定蓖麻毒素的串联质谱库 ...
- (原)Non-local Neural Networks
转载请注明出处: 论文: https://arxiv.org/abs/1711.07971 第三方pytorch代码: https://github.com/AlexHex7/Non-local_py ...
- 嘉泽 P2120: 【基础】半质数 题解
原题链接 简要题意: 求区间内能分解为两个质数乘积的数. 欧拉筛先筛素数. 然后再筛答案. 时间复杂度: \(O(n)\). 实际得分:\(100pts\). #pragma GCC optimize ...
- mybatis类型转换器 - 自定义全局转换enum
在数据模型.接口参数等场景部分属性参数为一些常量值,比如性别:男.女.若是定义成int或String类型,于是类型本身的范围太宽,要求使用者需要了解底层的业务方可知如何传值,那整体来看增加沟通成本,对 ...
- 子序列宽度求和 Sum of Subsequence Widths
2019-10-14 17:00:10 问题描述: 问题求解: 如果暴力求解,时间复杂度是exponational的,因为这里是子序列而不是子数组.显然,直接枚举子序列是不太现实的了,那么可以怎么做呢 ...
- Mysql中的三类锁,你知道吗?
导读 正所谓有人(锁)的地方就有江湖(事务),人在江湖飘,怎能一无所知? 今天来细说一下Mysql中的三类锁,分别是全局锁.表级锁.行级锁. 文章首发于作者公众号[码猿技术专栏],原创不易,喜欢的点个 ...
- K:缓存相关问题
缓存的作用在于提高程序的响应速度,一般用于程序存储运算所需数据或程序运算后的结果,以便再次访问或运算相同的程序(数据同样相同)时,能够得到快速的响应(适用于读多写少的场景).在现代计算机体系结构中,根 ...