Go封包捕获规则与defer有何区别? [英] How are Go closure capture rules distinct from defer?

查看:59
本文介绍了Go封包捕获规则与defer有何区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下Go代码演示了延缓和go闭包之间在闭包捕获规则中的区别.在一个教程中,有人告诉我for循环变量的作用域仅限于循环主体,但此处似乎有所不同.

The following Go code demonstrates a distinction in the closure-capture rules between defer and go closures. In a tutorial, I was told that for-loop variables have scope limited to the loop body, but something seems different here.

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

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

If the difference is intentional, then what are the different use-cases that justify it?

推荐答案

延期规范对此很清楚.对于那些关心诸如可变捕获规则之类的东西并且对于它的内容而言相对简短的人,该规范通常是至关重要的阅读.它说的是:

The spec for defer is clear on this. The spec in general is crucial reading for anyone who cares about stuff like variable capture rules, and relatively short for what it is. What it says here is:

每次执行"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.

有关延迟,恐慌和恢复的博客文章也对此进行了介绍

总是很难证明设计决策能使每个人都满意,特别是 defer 确实会让人们不时感到惊讶,尤其是当您进行 defer f1(f2(x)),而忘记了 f2(x)将会被立即评估.

It's always hard to justify design decisions to everyone's satisfaction, and the defer one in particular does surprise people now and then, notably when you do defer f1(f2(x)) and forget f2(x) will be evaluated immediately.

也许可以记住它的一件事是,在 defer (或 go )之后出现的是一个函数调用,并且只是功能调用本身(甚至不评估参数)会随时间推移.闭包定义了一个代码块,并且在执行代码时访问变量.

One thing that may help remember it, though, is that what comes after defer (or go) is a function call, and just the function invocation itself (not even evaluation of params) is shifted in time. The closure defines a block of code, and accessing the variables happens when the code is executed.

由于您对闭包所使用的变量捕获类型的使用感到好奇,因此,使用闭包与诸如 ForEach 之类的方法一起使用时,一个有用的例子是:

Since you were curious about the use for the kind of variable capture you get with closures, one example of where it's useful is if you're using a closure with a method like ForEach:

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

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

It's nice if, like in a regular for loop, the code between the braces can modify variables in the outer scope. That requires that the closure be accessing the variables, not just their values as of a specific time.

这篇关于Go封包捕获规则与defer有何区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆