GO 1.20 新功能:多重错误包装
预计将于 2023 年 2 月发布的 Go 1.20 有一个小的变化,对于那些大量使用错误包装的应用程序来说,可能会有效改进它们的错误处理方法。
让我们看一下它的用法,但首先,需要简要回顾一下什么是错误包装。如果你已经掌握了可以直接跳到下面的 “Go 1.20 新功能” 部分以获取新的信息。
(资料图片)
Go 中的错误是实现一个非常简单的接口:
typeerrorinterface{Error()string}
错误类型可以是任何东西,从string本身到int,但通常它们是struct类型。下面这个例子来自标准库:
typeerrstruct{sstring}func(e*err)Error()string{returne.s}
要检查 Go 中的错误,你只需比较一个值(在本例中为int值):
iferr==io.EOF{//...}
第二种常见的用法是检查错误的类型,那也意味着要写更多的代码:
ifnerr,ok:=err.(net.Error){//...(usenerrwhichisanet.Error)}
在上面的例子中,类型断言测试类型net.Error的err值,并创建一个新变量nerr,它可以在 if 语句中使用。Go 中的错误方便理解、易于使用且非常高效。
错误包装从 Go 1.13 开始,引入了错误包装。包装允许将错误嵌入到其他错误中,就像在其他语言中包装异常一样。这非常实用,比如函数遇到 “record not found” 错误时,可以向错误信息中添加更多上下文信息,例如 “unknown user: record not found”。
Go 中错误包装设计背后的有趣想法是:契约不用关心错误类型、结构或它们是如何创建的。而唯一关注的是解包过程和转换为字符串,因为这两者是必须的。这就非常容易实现:支持解包的错误类型必须实现Unwrap() error方法。
标准库中没有(命名的)接口可以向您展示,因为接口是隐式实现的,没有必要单独写一个。这里我们写一个只是为了更好说明这篇文章:
typeWrappedErrorinterface{Unwrap()error}
我们来看看 Go 标准库(实际上是 package fmt)中是如何实现包装错误的:
typewrapErrorstruct{msgstringerrerror}func(e*wrapError)Error()string{returne.msg}func(e*wrapError)Unwrap()error{returne.err}
由于上面错误类型实现了Error() string方法,所以说 Go 中的错误实际上最终是字符串并没有错,因此需要一种创建这些字符串的良好机制。这就是标准库中的函数fmt.Errorf发挥作用的地方:
varRecordNotFoundErr=errors.New("notfound")constname,id="lzap",13werr:=fmt.Errorf("unknownuser%q(id%d):%w",name,id,recordNotFoundErr)fmt.Println(werr.Error())
一个特殊格式的动词%w,每次调用只能使用一次(稍后会详细介绍),用于错误参数。除此之外,该函数的工作方式类似于fmt.Printf函数。下面的例子打印了这个结果:
unknownuser"lzap"(id13):notfound
如你所见,错误包装本质上是一个链表。要解包错误,请使用errors.Unwrap函数,该函数将为链表中的最后一个错误值返回nil。要检查错误类型或值,需要遍历整个列表,这对于需要进行频繁的错误检查不太实用。幸运的是,有两个辅助函数可以做到这一点。
检查包装错误列表中的值:
iferrors.Is(err,RecordNotFoundErr){//...}
检查特定类型(下面例子是来自标准库的网络错误):
varnerr*net.Erroriferrors.As(err,&nerr){//...(usenerrwhichisa*net.Error)}
以上总结了 Go 1.13 及更高版本中的错误包装。
Go 1.20 新特性让我们看看 Go 1.20 中真正的新功能,从函数errors.Join开始,它通过可变参数包装错误列表:
err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)fmt.Println(err)
当事先不知道错误数量时,此功能可用于将错误连接在一起。一个很好的例子是从 goroutines 收集错误。值得一提的是,该函数将列表中的错误与换行符连接起来。上面的代码片段打印:
err1err2
对于许多应用程序或(日志记录)库来说,这可能会存在问题,它们期望错误通常只是没有换行符的字符串。幸运的是,Go 1.20 中的另一个变化改变了fmt.Errorf的行为:该函数现在接受多个%w格式说明符:
err1:=errors.New("err1")err2:=errors.New("err2")err:=fmt.Errorf("%w+%w",err1,err2)fmt.Println(err)
以前会导致格式错误的格式字符串现在可以正确打印:
err1+err2
同时包装多个错误实现Unwrap() error,这是可能的吗?
事实证明,在 Go 1.20 标准库中有一种新的机制: 实现Unwrap() []error函数的错误类型可以包装多个错误。让我们来看看这是如何在库中实现的:
typejoinErrorstruct{errs[]error}func(e*joinError)Error()string{//concatenateerrorswithanewlinecharacter}func(e*joinError)Unwrap()[]error{returne.errs}
一个理论上的接口,但标准库中实际不存在,如下所示:
typeMultiWrappedErrorinterface{Unwrap()[]error}
由于 Go 不允许方法重载,因此每种类型都可以实现Unwrap() error或Unwrap() []error,但不能同时实现。还记得我提到过包装错误本质上是一个链表吗?实现前一个(新引入的)方法的类型实际上形成了一个链接树,函数errors.Is和errors.As的工作方式相同,只是现在它们需要遍历树而不是列表。根据文档,该实现执行预排序、深度优先遍历。
这确实是 Go 1.20 带来的全部,它可能看起来是一个小的变化,但它提供了如何有效和干净地处理错误的新方法。在展示真实示例之前,让我总结一下新功能:
新的Unwrap []error函数契约允许遍历错误树。
新的errors.Join函数,这是一个方便的函数,用于连接两个错误字符串值(使用换行符)。
现有函数errors.Is和errors.As已更新,可以同时处理错误列表和错误树。
现有函数fmt.Errorf现在接受多个%w格式动词。实践上面这一切都很棒,但是你如何在实践中利用它呢?
在一个小型 REST API 微服务中,我们通过errors.New和fmt.Errorf处理来自 DAO 包(数据库)、REST 客户端(其他后端服务)和其他包的各种错误。返回的 HTTP 状态代码应该是 2xx、4xx 或 5xx,具体取决于错误状态以遵循最佳 REST API 实践。实现此过程的一种方法是解开主 HTTP 处理程序中的错误并找出它是哪种错误。
然而,通过多重错误包装,现在可以包装根本原因(例如数据库返回 “no records found” )和返回给用户 HTTP 代码(在本例中为 404)。
一个工作示例如下所示:
packagemainimport("errors""fmt")//commonHTTPstatuscodesvarNotFoundHTTPCode=errors.New("404")varUnauthorizedHTTPCode=errors.New("401")//databaseerrorsvarRecordNotFoundErr=errors.New("DB:recordnotfound")varAffectedRecordsMismatchErr=errors.New("DB:affectedrecordsmismatch")//HTTPclienterrorsvarResourceNotFoundErr=errors.New("HTTPclient:resourcenotfound")varResourceUnauthorizedErr=errors.New("HTTPclient:unauthorized")//applicationerrors(thenewfeature)varUserNotFoundErr=fmt.Errorf("usernotfound:%w(%w)",RecordNotFoundErr,NotFoundHTTPCode)varOtherResourceUnauthorizedErr=fmt.Errorf("unauthorizedcall:%w(%w)",ResourceUnauthorizedErr,UnauthorizedHTTPCode)funchandleError(errerror){iferrors.Is(err,NotFoundHTTPCode){fmt.Println("Willreturn404")}elseiferrors.Is(err,UnauthorizedHTTPCode){fmt.Println("Willreturn401")}else{fmt.Println("Willreturn500")}fmt.Println(err.Error())}funcmain(){handleError(UserNotFoundErr)handleError(OtherResourceUnauthorizedErr)}
这将打印:
Willreturn404usernotfound:DB:recordnotfound(404)Willreturn401unauthorizedtocallotherservice:HTTPclient:unauthorized(401)
从这样的人工代码片段中可能看起来不太明显的是,实际上的错误声明通常分布在许多包中,并且不容易跟踪所有可能的错误以确保所需的 HTTP 状态代码。在这种方法中,所有在一个地方声明的应用程序级包装错误也包含了 HTTP 代码。
请注意,这在 Go 1.19 或更早版本中是不可能的,因为fmt.Errorf函数只会包装第一个错误。该代码确实在 1.19 上可以编译,甚至不会产生运行时恐慌,但它实际上不会工作。
显然,常见的 HTTP 状态代码很容易成为一种新的错误类型(基于int类型),因此可以通过errors.As轻松提取实际代码,但我想让示例保持简单。
Feel free to play around with the code on Go Playground. Make sure to use “dev branch” or 1.20+ version of Go. 可以在 Go Playground 上自由运行上述代码。确保使用 “dev branch” 或 Go 的 1.20+ 版本。现有应用在你的应用程序中实施新功能时,请注意errors.Unwrap函数。对于具有Unwrap() []error的错误类型,它总是返回nil:
err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)unwrapped:=errors.Unwrap(err)fmt.Println(unwrapped)
由于 Go 1.X 兼容性承诺,这会打印出 “nil”。当你引入多个包装错误时,请确保检查展开代码。幸运的是,典型 Go 代码中的大部分错误检查都是使用errors.Is和errors.As完成的。
错误包装并不是 Go 中所有错误处理的最终解决方案。它只是提供了一种干净的方法来处理典型 Go 应用程序中的错误,对于简单应用程序来说也许就完全足够了。原文地址:https://lukas.zapletalovi.com/posts/2022/wrapping-multiple-errors/原文作者:Lukáš Zapletal本文永久链接:https://github.com/gocn/translator/blob/master/2022/w50_Wrapping_multiple_errors译者:haoheipi校对:watermelo
往期推荐
谷歌发布查找开源漏洞的Go工具OSV-Scanner最好的Go框架:没有框架?
「每周译Go」如何在Go中构造For 循环想要了解Go更多内容,欢迎扫描下方关注公众号,回复关键词 [实战群],就有机会进群和我们进行交流
分享、在看与点赞Go
标签:

GO 1.20 新功能:多重错误包装
2022-12-27

共同药业(300966)12月26日主力资金净卖出1462.07万元
2022-12-27

浙江艾能聚光伏科技股份有限公司已提交注册_全球播报
2022-12-26

华维设计12月26日下跌,股价创历史新低
2022-12-26

天天即时看!飞马国际(002210)12月23日主力资金净卖出5002.94万元
2022-12-26

全球通讯!加强组织统筹优化就诊流程 保障患者就医
2022-12-25

世界报道:任达华带妻女马尔代夫度假,女儿穿吊带身材火辣,气场不输超模!
2022-12-25

每日时讯!奥翔药业(603229)12月23日主力资金净卖出1575.93万元
2022-12-24

锦龙股份: 关于股东股份解除质押及质押的公告
2022-12-23

三生(中国)第三度荣获“年度匠心品质企业”称号|全球新视野
2022-12-23
共同药业(300966)12月26日主力资金净卖出1462.07万元
浙江艾能聚光伏科技股份有限公司已提交注册_全球播报
华维设计12月26日下跌,股价创历史新低
天天即时看!飞马国际(002210)12月23日主力资金净卖出5002.94万元
全球通讯!加强组织统筹优化就诊流程 保障患者就医
世界报道:任达华带妻女马尔代夫度假,女儿穿吊带身材火辣,气场不输超模!
每日时讯!奥翔药业(603229)12月23日主力资金净卖出1575.93万元
锦龙股份: 关于股东股份解除质押及质押的公告
三生(中国)第三度荣获“年度匠心品质企业”称号|全球新视野
世界速读:【机构调研记录】中科沃土基金调研箭牌家居、京山轻机
今日聚焦!钱又流回了房地产
陕西:234种新冠用药临时纳入医保支付范围
中邮钱包贷款逾期1个月多久会上征信_天天快消息
莎普爱思(603168):莎普爱思持股5%以上股东、原董事集中竞价减持股份结果
【全球新视野】两新进镇村 结对促振兴
【天天热闻】输卵管堵塞危害大 三大方法可治疗“堵车”
有友食品: 有友食品关于参加重庆辖区2022年投资者网上集体接待日活动的公告-天天快播报
食品级牙膏误导消费 消保委调解商家退款
西安治疗宫颈炎哪家医院好?陕西渭南哪家妇科医院好 环球热点
大中矿业荣获和讯第20届中国财经风云榜“2022年度卓越价值上市公司”奖项
成都市人民政府关于冉亚林免职的通知
【环球报资讯】禹州市生态环境局:汇聚攻坚力量 助力疫情防控
8胜0负队史最佳开局!字母哥26+13+11雄鹿擒森林狼
美国监管机构对通用汽车旗下Cruise自动驾驶系统展开调查 天天热讯
厦门市市场监管局抽检化妆品120批次 全部合格
本周盘点(12.12-12.16):海螺水泥周跌2.02%,主力资金合计净流出4625.01万元
网贷逾期21天还不起会有哪些严重后果_天天快资讯
日防卫省宣称俄2架轰炸机朝日本领空飞行,日空自战机紧急升空
正业科技投资设立新公司 经营范围含半导体器件专用设备制造


- 普天科技董秘回复:公司目前重点发展北斗行业应用_天天最新
- 我国已推出263款惠民保产品 共覆盖29个省级行政区
- 观速讯丨金利集团四项科技成果达到国际先进水平
- 谈判仍在继续 欧盟将天然气上限决定推迟到下周一
- 拓普集团拟定增募资不超过40亿元 去年初定增募20亿-全球新视野
- 每日焦点!豫能控股:12月12日获融资买入248.60万元
- 妙可蓝多: 关于修订《公司章程》的公告
- 探访“学习困难”门诊:“我没想到这会是一种障碍” 世界滚动
- 北京昨日新增784例本土确诊病例和877例无症状感染者|环球速看料
- “百年中药老字号”又有大动作 动态
- 房企融资迎“第三支箭” 投行全力以赴同时坦言仍有难点|环球快资讯
- 股票行情快报:航天信息(600271)12月6日主力资金净卖出1171.35万元
- 进入了发展快车道 冷链行业市场规模正在快速膨胀
- 行业正站在风口 数字化时代在为传统的自行车产业赋能
- 以做强实体经济支撑为重点 成都单个项目年度计划投资同比提升
- 拥有多个国际赛事的直播版权 广州游戏电竞企业业绩向好
- 投诉量激增 直播带货存在这么多问题的主要原因是什么?
- 工作专班深入到各企业 春寒料峭挡不住松原市施工热情
- 引导企业向提供“产品+服务”转变 湖南加快智能农机服务化转型
- 创新平台建设和科技成果转化 德州加大力度重奖创新
- 潜在风险进一步放大 商品房现房销售已是大势所趋
- 有序复工复产 1—2月份工业经济发展新动能持续增强
- 多层次高频调度 1至2月河北省工业运行先行指标稳中有增
- 以车路协同为基础 智能交通推动城市交通绿色高质量发展
- 人才短板成为制约产业链高质量发展的关键节点
- 通过技术手段整合调配供给资源 家政行业不断提质扩容
- 强化产业链深层次合作 加强重大装备国产化“一条龙”模式构建
- 如何进一步提升纳税人缴费人的减税降费获得感?
- 探索建设大数据及网络安全示范试点城市有哪些积极意义?
- 对制造业中小微企业实施缓缴税费政策有哪些积极意义?
- 进一步增强自我保护意识 消费者需注意辨别谨慎消费
- 将“走出去”变“请进来” 西安贸易产业转移承接作用不断得到增强
- 厦门应如何融入“数字中国”的重大战略发展大局?
- 江苏省如何不断满足老人日益增长的养老服务需求?
- 建设一体化的职业健康信息管理平台 天津职业人群保障加强
- 潜力持续释放 1—2月乡村消费品市场恢复略好于城镇
- 直接对接社会化服务 楼宇调解室将整体提升青岛劳动争议水平
- 成功化解纠纷11.47万件 银保监会服务质量日趋提高
- 春雷响百虫出 惊蛰文化在其他方面有了进一步发展
- 青绿山水画在古代山水画发展史上有着怎样的影响与地位?
- 开播即爆款 “文化类节目收视率低”这一固有印象被推翻
- 涵盖了109件真迹作品 凯斯·哈林展览将持续至6月13日
- 带有一点自信的自嘲 “隔路”是另一种味道的“凡尔赛”
- 与文渊阁前后呼应 “何以中国”特展隆重致敬文化大成
- 严重者可造成暂时性失明 享受冰雪运动要注意眼睛的健康防护
- 种类繁多让人眼花缭乱 选购牛奶时需要重点关注什么?
- 网课让孩子感到不安焦虑怎么办?八问八答回应广大家长关切
- 循环系统很容易受到刺激 “倒春寒”期间老人该如何做?
- 青少年患者睡眠问题日趋增加 9条建议为孩子助眠
- 我国肥胖人群正逐年递增 不良饮食习惯是重要诱因