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

发布于 1周前 作者 sinazl 来自 Go语言

大家好,我是 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 语句时,还遇到过哪些陷阱?


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

更多关于Golang Go语言中 defer 使用时有哪些陷阱?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

闭包就闭包,又来个预处理

更多关于Golang Go语言中 defer 使用时有哪些陷阱?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


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
}

结果输出什么?

理解的问题吧,
和 C++的 RAII 类似,defer 是遵循执行顺序的,也当然支持条件语句,
所以只有逻辑执行到了才会触发。
golang 的这个特性我觉得挺好的,包括并发的时候 happens-before ,逻辑可见即所得

实际使用中 defer 在 return 之后的情况很多

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

正因为 defer 这个特性,如果打开失败了,也就不用关闭了

虽然我知道这个系列质量不高,但还是忍不住吐槽:这也叫陷阱吗……

发一个

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

我最初犯的错误是在代码块(花括号)中使用 defer 了,后来才发现这货只针对函数调用,草率了…

严格来说这个是闭包的问题,不是 defer 的问题

对于新手来说最大的陷阱就是,defer 语句中的变量,在 defer 声明时就决定了。

在Go语言中,defer语句用于延迟执行一个函数或方法,直到包含该defer语句的函数执行完毕时才会执行。尽管defer非常有用,但在使用时仍需注意以下几个陷阱:

  1. 延迟执行顺序defer语句是按后进先出的顺序执行的,即后定义的defer会先执行。这可能会与直觉相悖,特别是在处理资源释放等操作时容易出错。

  2. 修改函数参数:如果在defer语句之前修改了函数的参数(特别是引用类型),defer语句中使用的参数值将是修改后的值。这可能导致资源泄露或意外的行为。

  3. 忽略错误:在使用defer进行文件关闭、网络连接释放等操作时,如果忽略defer语句返回的错误,可能会掩盖潜在的问题。务必检查defer调用的返回值。

  4. 嵌套函数中的defer:在嵌套函数中使用defer时,需要特别注意其执行时机和上下文环境,以避免资源泄露或重复释放。

  5. 内存占用:大量使用defer可能会导致内存占用增加,因为每个defer语句都会占用一定的栈空间。在性能敏感的场景中,应谨慎使用。

总之,defer是Go语言中强大的特性,但使用时需仔细考虑其执行顺序、参数修改、错误处理等方面,以避免潜在的陷阱和错误。在编写代码时,务必进行充分的测试和验证,以确保defer语句的正确性和有效性。

回到顶部