翔云

Just try, don't shy. 最新文章请点击
随笔 - 294, 文章 - 0, 评论 - 27, 阅读 - 49万
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

golang defer那些坑

Posted on   翔云123456  阅读(809)  评论(0编辑  收藏  举报

defer以下几个特性,使用时需要关注下。

  • 即时的参数传递
  • 调用os.Exit()时defer不会被执行
  • defer与return的先后顺序

1.即时的参数传递

定义defer时传入的参数,是作为拷贝传递的。
也就是说,如果原来的变量值发生变化,不会影响传给defer的参数。

例子如下:

package main

import (
        "fmt"
)


func main(){

        test()

}

func test() {
        a := 0

        defer func (i int) {
                fmt.Println("in defer i:", i)
        }(a)

        a += 1
        fmt.Println("a:", a)
}

输出结果:

a: 1
in defer i: 0

可以看到,即使变量a发生变化,延迟执行时变量的值仍然是0,与定义defer时传入的值一样。

2.调用os.Exit()时defer不会被执行

当发生panic时,defer会被执行,但是当调用os.Exit()方法退出程序时,defer并不会被执行。

package main

import (
        "fmt"
        "os"
)


func main(){
        fmt.Println("main start")
        test()
}

func test() () {
        defer func () {
                fmt.Println("in defer ... ")
        }()

        os.Exit(0)
}

输出结果:

main start

defer定义的内容没有输出。

3.defer 与 return先后顺序

先来看两个例子:一个是返回匿名变量,一个是返回命名变量。

3.1 返回匿名变量

package main

import (
        "fmt"
)


func main(){
        i := test()
        fmt.Println("main i:", i)
}

func test() int {
        a := 0
        defer func () {
                a = 2
        }()

        a = 1
        return a
}

定义a为0, 接着修改为1,最后在defer中将a修改为2。

在main中返回的值仍然是1.

输出结果:

main i: 1

3.2 返回命名变量

package main

import (
        "fmt"
)


func main(){
        i := test()
        fmt.Println("main i:", i)
}

func test() (a int) {
        defer func () {
                a = 2
        }()

        a = 1
        return a
}

defer中修改a为2,能够返回给调用方。

输出结果:

main i: 2

实际上,defer 函数的执行既不是在 return 之后也不是在 return 之前,而是 return 语句包含了对 defer 函数的调用,即 return 会被翻译成如下几条伪指令:

保存返回值到栈上(如果是匿名变量,需要定义变量并赋值)
调用defer函数(如果有defer函数,则调用并执行)
调整函数栈
retq指令返回(如果是匿名变量,直接返回新定义的变量,如果是命名变量,直接返回命名变量)

命名变量返回时,不会创建新的变量,所以defer的修改会返回去。
而匿名变量,会创建新的变量,defer中的修改,还是修改原来的变量,所以修改不能返回去。

4.参考

Defer, Panic, and Recover

https://juejin.im/post/5b9b4acde51d450e5071d51f

https://my.oschina.net/henrylee2cn/blog/505535

编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示