首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Golang中defer 的五个坑-第三部分

译注:全文总共有四篇,本文为同系列文章的第三篇

本文将侧重于讲解使用 defer 的一些技巧

如果你对 defer 的基本操作还没有清晰的认识,请先阅读这篇文章(GCTT 出品的译文 https://studygolang.com/articles/11907 )。

#1 —— 在延迟调用函数的外部使用 recover

你总是应该在被延迟函数的内部调用 ,当出现一个panic异常时,在defer外调用

将无法捕获这个异常,而且 的返回值会是nil。

例子

输出

recover并没有成功捕获异常。

解决方案

在延迟调用的函数内部使用 就能够避免这个问题。

输出

#2 —— 在错误的位置使用 defer

这个陷阱来自于这篇 Go 的 50 个阴影。

例子

当 失败时会抛出异常。

输出

发生了什么?

因为在这里我们并没有检查我们的请求是否成功执行,当它失败的时候,我们访问了Body中的空变量res,因此会抛出异常

解决方案

总是在一次成功的资源分配下面使用defer,对于这种情况来说意味着:当且仅当http.Get成功执行时才使用defer

在上述的代码中,当有错误的时候,err会被返回,否则当整个函数返回的时候,会关闭res.Body。

旁注 1

在这里,你同样需要检查resp的值是否为nil,这是http.Get中的一个警告。通常情况下,出错的时候,返回的内容应为空并且错误会被返回,可当你获得的是一个重定向error时,resp的值并不会为nil,但其又会将错误返回。上面的代码保证了无论如何Body都会被关闭,如果你没有打算使用其中的数据,那么你还需要丢弃已经接收的数据。更多 详情。

#3 —— 不检查错误

简单地将清理的逻辑委托给defer并不意味着资源的释放就万无一失了,你也可能会错失有用的报错信息,让一些潜在的问题石沉大海。

反面教材

在这里, 可能会返回一个错误,可这个错误会被我们忽略掉

改进一下

最好还是检查可能的错误而不是直接交给defer就完事,你可以把defer内的代码写成一个帮助函数来简化我们的代码,这里为了讲解方便就没有进行简化。

再改进一下

你也可以通过命名的返回变量来返回defer内的错误。

旁注 2

你可以使用这个 包 来整合多个不同的错误,这会非常必要因为 defer 中的f.Close可能会把之前的错误也覆盖掉,将多个错误包裹在一起能够将所有的错误信息都写入日志,在诊断问题的时候能有更多的依据。

你也可以使用这个 包 来查看你遗漏的本应该检查错误的地方。

#4 —— 释放相同的资源

在第三小节中有一个小小的警告:如果你尝试使用相同的变量释放不同的资源,那么这个操作可能无法正常执行。

例子

这段看似没什么问题的代码尝试第二次关闭相同的资源。第二个变量 f会被关闭两次,因为f 变量会因第二个资源而改变它的值

输出

发生了什么

正如我们所看到的,当延迟函数执行时,只有最后一个变量会被用到,因此,f 变量会成为最后那个资源 (another-book.txt)。而且两个defer都会将这个资源作为最后的资源来关闭

解决方案

输出

你也可以使用函数来避免上述问题的发生,参考我在 这里 讲过的开闭模式。

#5 —— panic/recover 会取得并返回任意类型

你可能认为你总是需要往panic中传string或error类型的数据

传入 string

输出

传入 error

输出

传入任意类型

正如你所看到的panic可以接收string以及 error类型 。这意味着事实上你可以给 panic 传 “任意类型” 的数据并能够在defer中使用recover来获取这个数据。

为什么可以这么写?

这是因为panic的函数签名显示它可以接收interface{}类型,我们可以将它理解为 Go 中的 “任意类型”

这是panic的签名

recover的签名

因此,基本上它会这样运行

recover会把传入panic的值返回出来

这一部分就先告一段落了,我们第四部分见!

via: https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1

作者:Inanc Gumus

译者:yujiahaol68

校对:polaris1119

本文由 GCTT 原创编译,Go 中文网 荣誉推出

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180201G0BF3L00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券