Go defer 的运行时机和遇到的坑

马谦马谦马谦 Golang评论1,003字数 1212阅读 4 分 2 秒阅读模式

一、 defer 用法

defer 是 golang 中独有的流程控制语句,用于延迟指定语句的运行时机,运行于函数的内部,当他所属函数运行完之后它才会被调用。

例如以下使用 defer 的代码:

输出结果为:

它会先打印出 HelloWorld ,然后再打印出 HelloDefer 。一个函数中如果有多个 defer ,运行顺序和函数中的调用顺序相反,因为它们都是被写在了栈中:

运行结果:

二、 defer 和 return

在包含有 return 语句的函数中,defer 的运行顺序位于 return 之后,但是 defer 所运行的代码片段会生效:

运行结果:

这里很明显就能看到 defer 是在 return 之后运行的!但是有一个问题是 defer 里执行了语句 i += 1 ,按照这个逻辑的话返回的 i 值应该是 2 而不是 1 。这个问题是由于 return 的运行机制导致的:return 返回一个对象时,如果返回类型不是指针或者引用类型,那么 return 返回的就不是这个对象本身,而是这个对象的副本。也就是说,实际上当 defer 执行的时候,return 已经把值返回了,返回的是一个副本值,此时 defer 对值的修改就无效了。

如果返回的是引用值,defer 的改动是有效的。我们可以把返回值得 int 类型改成*int:

程序的输出为:

可以看到,在 return 和 defer 执行前,i 的值都是 1,并且地址一样。返回到 main 函数后,i 的地址也是和函数体的一样,但是值是 2 了,这就说明 defer 中对 i 的修改生效了。

三、 defer 和 panic

panic 会在 defer 运行完之后才把恐慌扩散到其他函数:

结果:

四、 defer 和 for 循环

不要在 defer 内使用外部变量,可能会造成一些意想不到的错误:

它的输出的结果是 55555 ,原理很简单,因为 defer 会在 for 循环运行完后才会调用,for 循环运行完时 i 的值为 5,所以打印的 i 值会是 55555 。

正确的做法是不要在 defer 函数中使用共享变量,而是手动传入参数:

此时就会打印出 43210 而不是 55555 了。

  最后更新:2020-2-22
马谦马谦马谦
  • 本文由 马谦马谦马谦 发表于 2017 年 11 月 29 日 22:03:44
  • 转载请务必保留本文链接:https://www.dyxmq.cn/program/code/golang/golang-defer.html
解决gvm工具无法下载安装包的问题 Golang

解决 gvm 工具无法下载安装包的问题

GVM(Go Version Manager) 是一款用于管理和切换不同 Go 语言版本的工具。它允许用户在同一台计算机上轻松安装、使用和管理多个 Go 版本,同时还能确保项目之间的依赖关系井然有序。 GVM 的主...
goland配置proto文件搜索路径 Golang

goland 配置 proto 文件搜索路径

默认情况下,goland(jetbrains 家的软件:idea 、 pycharm 、 phpstorm 以及 webstorm 都是一样) 安装 protobuf 插件后只会在一个特定的库路径下搜索 proto 文件。如果...
国内几个优质的Go Module代理仓库服务 Golang

国内几个优质的 Go Module 代理仓库服务

一、简介 go module 公共代理仓库,代理并缓存 go 模块。你可以利用该代理来避免 DNS 污染导致的模块拉取缓慢或失败的问题,加速你的构建。 简单来说就是国内访问被墙,go get 无法在线获取到仓库,...
Golang中glog库的使用方法 Golang

Golang 中 glog 库的使用方法

一、关于 glog 库 golang 中的 glog 库是 google 著名开源 C++日志库 glog 的 golang 版本,在 golang 默认日志库的基础上做了更进一层的封装使得该库能更贴近日常使用。项目首页为 gol...
匿名

发表评论

匿名网友
:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:
确定

拖动滑块以完成验证