defer的陷阱

当使用defer时,请记住如下几点:延迟函数在函数的末尾调用;延迟语句的作用域为函数级别,而不是代码块级别。

换句话说:延迟调用只在函数结束退出时执行,并不在iffor语句创建的代码块中执行。

func main() {
	fmt.Println("Before if")
	if true {
		defer fmt.Println("inside if")
	}
	fmt.Println("Ater if")
}

// ----Output-----
// Before if
// Ater if
// inside if

您也许以为上述延迟语句会在退出if分支时执行,但事实上它只在函数的最后被执行。

在延迟函数中使用外部变量

func main() {
	for i := 0; i < 2; i++ {
		defer func() {
			fmt.Printfln("%d", i)
		}()
	}
}

// ----Output----
// 2
// 2

一个常犯的错误是认为上述代码会输出01。当我们看到defer时,它们就是i的值。

但是,defer创建了一个闭包,它只捕获了i的引用,而不是变量的值。

我们看看上述代码实际会怎样执行就清楚了:

现在已经清楚了延迟调用fmt.Println时为何两次i的值都是2。

我们可以通过强制捕获变量来修正上述行为:

这样闭包的开销可能会稍微大些,因为它需要分配对象来收集由闭包捕获的所有变量。

最后更新于

这有帮助吗?