V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
frankphper
V2EX  ›  Go 编程语言

Go 语言中 defer 使用时有哪些陷阱?

  •  
  •   frankphper · 312 天前 · 2600 次点击
    这是一个创建于 312 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大家好,我是 frank ,「 Golang 语言开发栈」公众号作者。

    01 介绍

    defer 的使用方式是在其后紧跟一个函数调用或方法调用,确保在其所在的函数体返回之前执行其调用的函数或方法。

    在 Go 语言中,defer 一般用于资源释放,或使用 defer 调用一个匿名函数,在匿名函数中使用 recover() 处理异常 panic

    在使用 defer 时,也很容易遇到陷阱,本文我们介绍使用 defer 时有哪些陷阱。

    02 defer 陷阱

    defer 语句不可以在 return 语句之后。

    示例代码:

    func main() {
    	name := GetUserName("phper")
    	fmt.Printf("name:%s\n", name)
    	if name != "gopher" {
    		return
    	}
    	defer fmt.Println("this is a defer call")
    }
    
    func GetUserName(name string) string {
    	return name
    }
    

    输出结果:

    name:phper
    

    阅读上面这段代码,我们在 return 语句之后执行 defer 语句,通过输出结果可以发现 defer 语句调用未执行。

    虽然 defer 可以在函数体中的任意位置,我们也是需要特别注意使用 defer 的位置是否可以执行。

    defer 语句执行匿名函数,参数预处理。

    示例代码:

    func main() {
    	var count int64
    	defer func(data int64) {
    		fmt.Println("defer:", data)
    	}(count + 1)
    	count = 100
    	fmt.Println("main:", count)
    }
    

    输出结果:

    main: 100
    defer: 1
    

    阅读上面这段代码,首先我们定义一个类型为 int64 的变量 count,然后使用 defer 语句执行一个匿名函数,匿名函数传递参数为 count + 1,最终 main 函数输出 100,defer 执行的匿名函数输出 1

    因为在执行 defer 语句时,执行了 count + 1,并先将其存储,等到 defer 所在的函数体 main 执行完,再执行 defer 语句调用的匿名函数的函数体中的代码。

    03 总结

    本文主要介绍在使用 defer 语句时可能会遇到的陷阱。分别是 defer 语句不可以在 return 语句之后;defer 语句执行的匿名函数,匿名函数的参数会被预先处理。

    读者朋友们在使用 Go 语言的 defer 语句时,还遇到过哪些陷阱?

    9 条回复    2024-01-22 10:55:12 +08:00
    seers
        1
    seers  
       312 天前 via iPhone
    闭包就闭包,又来个预处理
    dyllen
        2
    dyllen  
       312 天前
    package main

    import "fmt"

    func main() {
    fmt.Println(getA(), getB())
    }

    func getA() (a int) {
    a += 1
    defer func() {
    a += 1
    }()
    return a
    }

    func getB() int {
    a := 0
    a += 1
    defer func() {
    a += 1
    }()
    return a
    }

    结果输出什么?
    AnroZ
        3
    AnroZ  
       312 天前
    理解的问题吧,
    和 C++的 RAII 类似,defer 是遵循执行顺序的,也当然支持条件语句,
    所以只有逻辑执行到了才会触发。
    golang 的这个特性我觉得挺好的,包括并发的时候 happens-before ,逻辑可见即所得
    zhujinliang
        4
    zhujinliang  
       312 天前   ❤️ 2
    实际使用中 defer 在 return 之后的情况很多

    比如
    f, err := os.Open("foo.txt")
    if err != nil {
    return err
    }
    defer f.Close()

    正因为 defer 这个特性,如果打开失败了,也就不用关闭了
    kneo
        5
    kneo  
       312 天前 via Android   ❤️ 4
    虽然我知道这个系列质量不高,但还是忍不住吐槽:这也叫陷阱吗……
    yplam
        6
    yplam  
       312 天前 via Android
    发一个

    ```

    func checkVal() (b bool) {
    defer func() {
    println("b", b)
    }()
    b = true
    return false
    }

    ```
    zhwguest
        7
    zhwguest  
       312 天前
    我最初犯的错误是在代码块(花括号)中使用 defer 了,后来才发现这货只针对函数调用,草率了....
    Carlgao
        8
    Carlgao  
       311 天前
    @yplam 严格来说这个是闭包的问题,不是 defer 的问题
    ted0220
        9
    ted0220  
       311 天前
    对于新手来说最大的陷阱就是,defer 语句中的变量,在 defer 声明时就决定了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   836 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 21:59 · PVG 05:59 · LAX 13:59 · JFK 16:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.