gpt4 book ai didi

go - Go 闭包捕获规则与 defer 有何不同?

转载 作者:数据小太阳 更新时间:2023-10-29 03:05:46 24 4
gpt4 key购买 nike

以下 Go 代码演示了 defer 和 go 闭包之间闭包捕获规则的区别。在教程中,有人告诉我 for 循环变量的范围仅限于循环体,但这里似乎有所不同。

package main

import "fmt"

func deferred() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}

func cps() {
clo := new(func())
*clo = func() {}
defer func() { (*clo)() }()
for i := 0; i < 5; i++ {
oldClo := *clo
*clo = func() {
fmt.Println(i)
oldClo()
}
}
}

func cpsCpy() {
clo := new(func())
*clo = func() {}
defer func() { (*clo)() }()
for i := 0; i < 5; i++ {
oldClo := *clo
cpy := i
*clo = func() {
fmt.Println(cpy)
oldClo()
}
}
}

func main() {
fmt.Println("defer")
deferred()
fmt.Println("cps")
cps()
fmt.Println("cpsCpy")
cpsCpy()
}

这会产生输出:

defer
4
3
2
1
0
cps
5
5
5
5
5
cpsCpy
4
3
2
1
0

如果差异是有意为之,那么可以证明差异的不同用例是什么?

最佳答案

spec for defer对此很清楚。一般来说,规范对于任何关心变量捕获规则之类的东西的人来说都是至关重要的阅读,而且它的内容相对较短。这里说的是:

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.

the blog post on defer, panic, and recover 中也有介绍.

总是很难证明每个人都满意的设计决策,而 defer 有时确实会让人们感到惊讶,特别是当您执行 defer f1(f2(x)) 而忘记 f2(x) 将立即被评估。

但有一件事可能有助于记住它,即 defer(或 go)之后发生的是函数调用,并且只是函数调用本身(甚至不是参数的评估)会及时移动。闭包定义了一个代码块,并且在执行代码时访问变量。


由于您对使用闭包获得的变量捕获类型感到好奇,如果您使用闭包和类似 ForEach 的方法,那么它的一个有用的例子是:

myBTree.ForEach(func (key, value []byte) {
//...
})

如果像在常规 for 循环中一样,大括号之间的代码可以修改外部作用域中的变量,那就太好了。这要求闭包访问变量,而不仅仅是它们在特定时间的值。

关于go - Go 闭包捕获规则与 defer 有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39537522/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com