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 ...
随机推荐
- Mysql优化大分页查询
如题,年前做了一个需求,涉及到Mysql大分页查询,整理一下,希望对需要的小伙伴有帮助. 背景分页查询的性能瓶颈B+树简述B+比起二叉查找树,有什么优势?分页查询过程测试集解决方法1 延迟关联法:2 ...
- main.c(53): error: #268: declaration may not appear after executable statement in block
这个问题是在编译STM32的程序时遇到的,这个错误的原因是对于变量的声明不能放在可执行语句后面,必须在主函数开头声明变量.在程序中声明一个变量时,需要在可执行语句之前声明,否则会出现以上错误.
- Blazor-断开连接后重新加载浏览器
在大多数情况下,Blazor将与以前相同的线路上重新连接到服务器.但有时无法重新连接,需要重新加载web浏览器才能使网站重新工作.如果服务器回收应用程序池,则需要手动重新加载页面在没有调试的情况下在I ...
- Python+Selenium+Unittest编写超链接点击测试用例
测试功能:博客园首页网站分类的一级菜单链接和二级菜单链接的点击. 遇到的问题: 1.循环点击二级菜单时,点击了一个一级菜单下的第一个二级菜单后,页面会刷新,再定位同一个一级菜单次下的第二个二级菜单时, ...
- 远程调试docker构建的weblogic
环境信息 OSType: CentOS Linux 7 (Core) x86_64 3.10.0-957.21.3.el7.x86_64 DockerVersion: 19.03.8 Mirrors: ...
- 被问到到http的时候你就这么回答!
大家好,我是标题党,啊不,我是小雨小雨,致力于分享有趣的.实用的技术文章. 内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步. 分享不易,希望能够得到大家的支持和关注. 什么是互 ...
- nmap端口扫描工具安装和使用方法
nmap(Network Mapper)是一款开源免费的针对大型网络的端口扫描工具,nmap可以检测目标主机是否在线.主机端口开放情况.检测主机运行的服务类型及版本信息.检测操作系统与设备类型等信息. ...
- UVa 11059 最大乘积 java 暴力破解
题目链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_proble ...
- shell脚本基础知识以及变量
一.基础知识 1.shell脚本的格式注意事项 第一行(一般必须写明):指定脚本使用的shell(若不写明也不影响脚本的执行,系统会自动以sh解析脚本)."#!/bin/bash" ...
- 一篇文章让你了解动态数组的数据结构的实现过程(Java 实现)
目录 数组基础简单回顾 二次封装数组类设计 基本设计 向数组中添加元素 在数组中查询元素和修改元素 数组中的包含.搜索和删除元素 使用泛型使该类更加通用(能够存放 "任意" 数据类 ...