本文主要跟大家介绍了Golang巧用defer进行错误处理的相关内容,分享出来供大家参考学习,下面来看看详细的介绍:

问题引入
毫无疑问,错误处理是程序的重要组成部分,有效且优雅的处理错误是大多数程序员的追求。很多程序员都有C/C++的编程背景,Golang的程序员也不例外,他们处理错误有意无意的带着C/C++的烙印。
我们看看下面的例子,就有一种似曾相识的赶脚,代码如下:
func deferDemo() error {
err := createResource1()
if err != nil {
return ERR_CREATE_RESOURCE1_FAILED
}
err = createResource2()
if err != nil {
destroyResource1()
return ERR_CREATE_RESOURCE2_FAILED
}
err = createResource3()
if err != nil {
destroyResource1()
destroyResource2()
return ERR_CREATE_RESOURCE3_FAILED
}
err = createResource4()
if err != nil {
destroyResource1()
destroyResource2()
destroyResource3()
return ERR_CREATE_RESOURCE4_FAILED
}
return nil
}
从代码的实现中可以看出:在一个函数中,当创建新资源失败时,则要清理所有前面已经创建成功的资源,这使得函数中有了重复代码的坏味道,比如destroyResource1函数调用了3次,destroyResource2函数调用了2次。
重构一:一个defer + 多个flag
Golang提供了一个很好用的关键字defer,当包含defer的函数执行完毕时(不管是通过return的正常结束,还是由于panic导致的异常结束),defer语句才被调用。
考虑到这一点,我们尝试将所有资源在defer语句中统一清理。由于函数返回时,不知道是否需要清理以及清理那些资源,所以要增加多个flag。
重构后的代码如下所示:
func deferDemo() error {
flag := false
flag1 := false
flag2 := false
flag3 := false
defer func() {
if !flag {
if flag3 {
destroyResource3()
}
if flag2 {
destroyResource2()
}
if flag1 {
destroyResource1()
}
}
}()
err := createResource1()
if err != nil {
return ERR_CREATE_RESOURCE1_FAILED
}
flag1 = true
err = createResource2()
if err != nil {
return ERR_CREATE_RESOURCE2_FAILED
}
flag2 = true
err = createResource3()
if err != nil {
return ERR_CREATE_RESOURCE3_FAILED
}
flag3 = true
err = createResource4()
if err != nil {
return ERR_CREATE_RESOURCE4_FAILED
}
flag = true
return nil
}
从重构后的代码可以看出,虽然消除了重复,但是引入了太多的flag:
显然,这不是我们想要的
重构二:多个defer
看过linux源码的同学都知道,在内核代码中,很多地方都通过goto语句来集中处理错误,非常优雅。
我们用这种方法将重构前的代码用C语言写一下,代码如下所示:
ErrCode deferDemo()
{
ErrCode err = createResource1();
if (err != ERR_SUCC)
{
goto err_1;
}
err = createResource2();
if (err != ERR_SUCC)
{
goto err_2;
}
err = createResource3();
if (err != ERR_SUCC)
{
goto err_3;
}
err = createResource4();
if (err != ERR_SUCC)
{
goto err_4;
}
return ERR_SUCC;
err_4:
destroyResource3();
err_3:
destroyResource2();
err_2:
destroyResource1();
err_1:
return ERR_FAIL;
}
没有重复,没有flag,错误处理也很优雅,感觉很爽,那以前在C/C++编码规范中禁止使用goto语句的规则确实有点过,呵呵...
从重构后的C代码中可以看出,create操作和destroy操作的顺序类似入栈和出栈的顺序:
于是我们又想到了defer语句:当Golang的代码执行时,如果遇到defer语句,则压入堆栈,当函数返回时,会按照后进先出的顺序调用defer语句。
我们看一个例子,代码如下所示:
func main() {
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
}
运行后,日志如下所示:
3 2 1
然而,有堆栈特性还不够,因为伴随着create操作,destroy操作入栈是有条件的:
可见,destroy操作的入栈条件是create操作成功,但是destroy操作并不是一定执行,只有当某个create操作失败("err != nil")时,前面入栈的destory操作才需要执行,所以err的值也需要入栈。然而,destroy操作入栈时"err == nil" ,于是问题就变成:当err的值在后面变成非nil时,应该同步修改堆栈中的err值,即堆栈中传递的是引用或指针而不是值。
当err的引用或指针和destroy操作都需要入栈时,defer后面必须是一个闭包调用。我们知道,对于闭包的参数是值传递,而对于外部变量却是引用传递。为了简单优雅起见,我们将err不通过参数的指针传递,而通过外部变量的引用传递。
我们根据这个结论重构一下代码,如下所示:
func deferDemo() error {
err := createResource1()
if err != nil {
return ERR_CREATE_RESOURCE1_FAILED
}
defer func() {
if err != nil {
destroyResource1()
}
}()
err = createResource2()
if err != nil {
return ERR_CREATE_RESOURCE2_FAILED
}
defer func() {
if err != nil {
destroyResource2()
}
}()
err = createResource3()
if err != nil {
return ERR_CREATE_RESOURCE3_FAILED
}
defer func() {
if err != nil {
destroyResource3()
}
}()
err = createResource4()
if err != nil {
return ERR_CREATE_RESOURCE4_FAILED
}
return nil
}
本次重构消除了代码的坏味道,不由的感叹一句:”升级了,我的哥!“
总结
本文通过巧用defer,有效且优雅的处理了错误,该技巧应该被所有的Golang程序员掌握并大量使用。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
# golang
# defer
# error
# 深入理解Go语言中的闭包
# 举例讲解Go语言中函数的闭包使用
# Go基础教程系列之回调函数和闭包详解
# Go 中闭包的底层原理
# Go程序员踩过的defer坑错误处理
# Go语言中的函数、闭包、defer、错误处理的学习教程
# 重构
# 所示
# 多个
# 可以看出
# 时才
# 巧用
# 的是
# 是一个
# 中统
# 也不
# 都有
# 好了
# 带着
# 太多
# 相关内容
# 是有
# 却是
# 一句
# 就有
# 也很
相关文章:
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
西安制作网站公司有哪些,西安货运司机用的最多的app或者网站是什么?
无锡制作网站公司有哪些,无锡优八网络科技有限公司介绍?
建站之星如何开启自定义404页面避免用户流失?
javascript中对象的定义、使用以及对象和原型链操作小结
网站制作新手教程,新手建设一个网站需要注意些什么?
如何获取开源自助建站系统免费下载链接?
建站IDE高效指南:快速搭建+SEO优化+自适应模板全解析
Bpmn 2.0的XML文件怎么画流程图
如何在阿里云香港服务器快速搭建网站?
网站网页制作电话怎么打,怎样安装和使用钉钉软件免费打电话?
如何零成本快速生成个人自助网站?
网站制作哪家好,cc、.co、.cm哪个域名更适合做网站?
哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?
简单实现Android文件上传
建站之星免费版是否永久可用?
建站之星安装后界面空白如何解决?
招商网站制作流程,网站招商广告语?
制作网站哪家好,cc、.co、.cm哪个域名更适合做网站?
如何快速搭建响应式可视化网站?
北京专业网站制作设计师招聘,北京白云观官方网站?
设计网站制作公司有哪些,制作网页教程?
html制作网站的步骤有哪些,iapp如何添加网页?
百度网页制作网站有哪些,谁能告诉我百度网站是怎么联系?
如何批量查询域名的建站时间记录?
如何高效生成建站之星成品网站源码?
名字制作网站免费,所有小说网站的名字?
如何快速搭建安全的FTP站点?
如何选购建站域名与空间?自助平台全解析
如何在腾讯云免费申请建站?
如何用搬瓦工VPS快速搭建个人网站?
商务网站制作工程师,从哪几个方面把握电子商务网站主页和页面的特色设计?
重庆网站制作公司哪家好,重庆中考招生办官方网站?
定制建站价位费用解析与套餐推荐全攻略
西安大型网站制作公司,西安招聘网站最好的是哪个?
存储型VPS适合搭建中小型网站吗?
网站微信制作软件,如何制作微信链接?
ppt制作免费网站有哪些,ppt模板免费下载网站?
怎么用手机制作网站链接,dw怎么把手机适应页面变成网页?
SQL查询语句优化的实用方法总结
建站主机选哪家性价比最高?
广州商城建站系统开发成本与周期如何控制?
如何在七牛云存储上搭建网站并设置自定义域名?
建站主机助手选型指南:2025年热门推荐与高效部署技巧
建站三合一如何选?哪家性价比更高?
专业制作网站的公司哪家好,建立一个公司网站的费用.有哪些部分,分别要多少钱?
如何快速重置建站主机并恢复默认配置?
如何在服务器上三步完成建站并提升流量?
简易网站制作视频教程,使用记事本编写一个简单的网页html文件?
*请认真填写需求信息,我们会在24小时内与您取得联系。