文档章节

Go 关键字 defer 的一些坑

云迹
 云迹
发布于 2017/09/03 02:23
字数 724
阅读 12
收藏 0
点赞 0
评论 0

什么是 defer?如何理解 defer 关键字?Go 中使用 defer 的一些坑。

defer 意为延迟,在 golang 中用于延迟执行一个函数。它可以帮助我们处理容易忽略的问题,如资源释放、连接关闭等。但在实际使用过程中,有一些需要注意的地方(坑),下面我们一一道来。

一些结论

首先,我们来了解 defer 的一些结论:

1、若函数中有多个 defer,其执行顺序为 先进后出,可以理解为栈。

GOpackage main

import "fmt"

func main() {
  for i := 0; i < 5; i++ {
    defer fmt.Println(i)
  }
}

Output:
4
3
2
1
0

2、return 会做几件事:

  1. 给返回值赋值
  2. 调用 defer 表达式
  3. 返回给调用函数
GOpackage main

import "fmt"

func main() {
    fmt.Println(increase(1))
}

func increase(d int) (ret int) {
  defer func() {
    ret++
  }()

  return d
}
  
Output:
2

3、若 defer 表达式有返回值,将会被丢弃。

更多请参考官方文档

闭包与匿名函数

匿名函数:没有函数名的函数。
闭包:可以使用另外一个函数作用域中的变量的函数。

在实际开发中,defer 的使用经常伴随着闭包与匿名函数的使用。小心踩坑哦:

GOpackage main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        defer func() {
            fmt.Println(i)
        }()
    }
}

Output:
5
5
5
5
5

解释一下,defer 表达式中的 i 是对 for 循环中 i 的引用。到最后,i 加到 5,故最后全部打印 5。

如果将 i 作为参数传入 defer 表达式中,在传入最初就会进行求值保存,只是没有执行延迟函数而已。

GOfor i := 0; i < 5; i++ {
    defer func(idx int) {
        fmt.Println(idx)
    }(i) // 传入的 i,会立即被求值保存为 idx
}

巩固一下

为了巩固一下上面的知识点,我们来思考几个例子。

例1:

GOfunc f() (result int) {
    defer func() {
        result++
    }()
    return 0
}

例2:

GOfunc f() (r int) {
    t := 5
    defer func() {
        t = t + 5
    }()
    return t
}

例3:

GOfunc f() (r int) {
    defer func(r int) {
        r = r + 5
    }(r)
    return 1
}

有没有得出结果?例1的答案不是 0,例2的答案不是 10,例3的答案也不是 6。

例1,比较简单,参考结论2,将 0 赋给 result,defer 延迟函数修改 result,最后返回给调用函数。正确答案是 1。

例2,defer 是在 t 赋值给 r 之后执行的,而 defer 延迟函数只改变了 t 的值,r 不变。正确答案 5。

例3,这里将 r 作为参数传入了 defer 表达式。故 func (r int) 中的 r 非 func f() (r int) 中的 r,只是参数命名相同而已。正确答案 1。

参考文档

[1] https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html

[2] http://golang.org/ref/spec#defer_statements

本文链接:https://deepzz.com/post/how-to-use-defer-in-golang.html参与评论 »

--EOF--

发表于 2017-08-27 02:08:00。

本站使用「署名 4.0 国际」创作共享协议,转载请注明作者及原网址。更多说明 »

本文转载自:https://deepzz.com/post/how-to-use-defer-in-golang.html

共有 人打赏支持
云迹
粉丝 3
博文 41
码字总数 4243
作品 0
成都
程序员
Golang defer 使用时的坑

defer是golang语言中的关键字,用于资源的释放,会在函数返回之前进行调用。一般采用如下模式: f,err := os.Open(filename)if err != nil { }defer f.Close() 如果有多个defer表达式,调用顺...

徐学良 ⋅ 2016/07/20 ⋅ 0

defer函数参数求值简要分析

defer函数参数求值简要分析 Tony Bai2018-03-231 阅读 FunctiondeferGo 一. 引子 书接上文,在发表了 《对一段Go语言代码输出结果的简要分析》 一文之后,原问题提出者又有了新问题,这是一个...

Tony Bai ⋅ 03/23 ⋅ 0

script的加载方式与执行

script一般是阻塞式加载的,H5新增了、和特性,可用于异步加载/延迟执行: async 属性是指当这个 script 可用时,就异步执行它 defer 属性是指当页面被解析完毕后,才能执行 如果以上两个属性...

一点灵犀 ⋅ 2016/06/16 ⋅ 0

Golang中defer、return、返回值之间执行顺序的坑

Go语言中延迟函数defer充当着 try...catch 的重任,使用起来也非常简便,然而在实际应用中,很多gopher并没有真正搞明白defer、return和返回值之间的执行顺序,从而掉进坑中,今天我们就来揭...

henrylee2cn ⋅ 2015/09/13 ⋅ 1

Swift中一些常见的关键字(inout,defer,throw等全家桶)

Swith关键字 Swift从1.0开始一直迭代到如今的4.0,出现了很多关键字。这些关键字,有的帮助我们处理了一些复杂的代码结构设计,有的帮助我们简化了许多代码。这帮助我们提高了开发的效率和降...

BennyLoo ⋅ 01/04 ⋅ 0

Go基础编程:延迟调用defer

1 defer作用 关键字 defer ⽤于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现在函数或方法的内部。 defer语句经常被用于处理成对的操作,如打开、关闭、...

tennysonsky ⋅ 2017/12/29 ⋅ 0

[渣翻译]use-package使用说明(version0.2)

use-package宏是spacemacs的精髓,实现了package配置的隔离,终于不用将所有的配置放在一起,乱得像一锅粥了,延迟加载极大地提升了spacemacs的启动性能。 (译者增加)新版无需在关键词后使...

兰穆达 ⋅ 2015/05/17 ⋅ 1

【go语言】wait,wait for me

去年输出了一系列golang的编码文章,但总感觉有话没讲,但又不清楚具体是什么,所以本文以随笔为主。 我们知道函数的调用其实就是一个入栈和出栈的动作: main() --> normal() 如果用这个表示...

qingkechina ⋅ 2017/02/05 ⋅ 0

go学习笔记——函数

函数 定义 可以给返回值命名,就像函数的输入参数一样。返回值被命名之后,它们的值在函数开始的时候被自动初始化为空。在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值...

Bluven ⋅ 2014/03/19 ⋅ 0

快看Sample代码,速学Swift语言(1)-语法速览

Swift是苹果推出的一个比较新的语言,它除了借鉴语言如C#、Java等内容外,好像还采用了很多JavaScript脚本里面的一些脚本语法,用起来感觉非常棒,作为一个使用C#多年的技术控,对这种比较超...

walb呀 ⋅ 2017/12/04 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JDK1.6和JDK1.7中,Collections.sort的区别,

背景 最近,项目正在集成测试阶段,项目在服务器上运行了一段时间,点击表格的列进行排序的时候,有的列排序正常,有的列在排序的时候,在后台会抛出如下异常,查询到不到数据,而且在另外一...

tsmyk0715 ⋅ 22分钟前 ⋅ 0

spring RESTful

spring RESTful官方文档:http://spring.io/guides/gs/rest-service/ 1. 可以这么去理解RESTful:其实就是web对外提供的一种基于URL、URI的资源供给服务。不是一个原理性知识点。是一个方法论...

BobwithB ⋅ 24分钟前 ⋅ 0

C++ 中命名空间的 5 个常见用法

相信小伙伴们对C++已经非常熟悉,但是对命名空间经常使用到的地方还不是很明白,这篇文章就针对命名空间这一块做了一个叙述。 命名空间在1995年被引入到 c++ 标准中,通常是这样定义的: 命名...

柳猫 ⋅ 26分钟前 ⋅ 0

@Conditional派生注解

@Conditional派生注解(Spring注解版原生的@Conditional作用) 作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效; @Conditional扩展注解 作用(判...

小致dad ⋅ 27分钟前 ⋅ 0

适配器模式

适配器模式 对象适配器 通过私有属性来实现的类适配器 通过继承来实现的接口适配器 通过继承一个默认实现的类实现的

Cobbage ⋅ 31分钟前 ⋅ 0

Java 限流策略

概要 在大数据量高并发访问时,经常会出现服务或接口面对暴涨的请求而不可用的情况,甚至引发连锁反映导致整个系统崩溃。此时你需要使用的技术手段之一就是限流,当请求达到一定的并发数或速...

轨迹_ ⋅ 35分钟前 ⋅ 0

GridView和子View之间的间隙

默认的情况下GridView和子View之间会有一个间隙,原因是GridView为了在子View被选中时在子View周围显示一个框。去掉的办法如下: android:listSelector="#0000" 或 setSelector(new ColorDra...

国仔饼 ⋅ 38分钟前 ⋅ 0

idea插件开发

1 刷新页面要使用多线程 2 调试要使用restart bug 不要去关闭调试的idea 否则再次启动会卡住

林伟琨 ⋅ 38分钟前 ⋅ 0

Java 内存模型

物理机并发处理方案 绝大多数计算任务,并不是单纯依赖 cpu 的计算完成,不可避免需要与内存交互,获取数据。内存要拿到数据,需要和硬盘发生 I/O 操作。计算机存储设备与 cpu 之间的处理速度...

长安一梦 ⋅ 45分钟前 ⋅ 0

思路分析 如何通过反射 给 bean entity 对象 的List 集合属性赋值?

其实 这块 大家 去 看 springmvc 源码 肯定可以找到实现办法。 因为 spirngmvc 的方法 是可以 为 对象 参数里面的 list 属性赋值的。 我也没有看 具体的 mvc 源码实现,我这里只是 写一个 简...

之渊 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部