zoukankan      html  css  js  c++  java
  • golang defer那些坑

    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

  • 相关阅读:
    错误总结Mapper扫描不到
    Spring项目出现--Error:java: Compilation failed: internal java compiler error
    mybatis-plus自动填充时间
    自定义异常类
    Swagger配置类
    JwtUtils工具类
    MD5加密工具类
    SpringBoot通用返回JSON数据封装类
    使用Swagger测试使传入json数据时出现JSON parse error: Unexpected character (‘}‘ (code 125)): was expecting double
    org.springframework.context.ApplicationContextException: Unable to start web server; nested exceptio
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/11074304.html
Copyright © 2011-2022 走看看