golang defer没有在预期时进行评估

Mik*_*SAT 1 variable-assignment go deferred

因此,根据规范,在调用defer函数时会计算defer函数中的值,但是在封闭函数返回之前不会执行这些操作.我得到了这个,并且理解了整个'for i:= 0; i <4; i ++ defer example打印3210'的事情.

但是,当我尝试使用延迟临时分配覆盖值(将max m分配给队列长度q)时,请确保在完成后重置值(示例为演示简化):

type tss struct {
    q int
    m int
}

func (t *tss) test() {
    if true {
        defer func() {
            t.q=t.q     //this should evaluate to 't.q = 50' right?
            fmt.Println("assigned",t.q,"to t.q")
        }()
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v\n", t.q, t.m)
}

func main() {
    ts := tss{50,1}
    fmt.Printf("q=%v, m=%v\n", ts.q, ts.m)
    ts.test()
    fmt.Printf("q=%v, m=%v\n", ts.q, ts.m)
}
Run Code Online (Sandbox Code Playgroud)

我希望收到输出:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1
Run Code Online (Sandbox Code Playgroud)

但实际输出是:

q=50, m=1
q=1, m=1
assigned 1 to t.q
q=1, m=1
Run Code Online (Sandbox Code Playgroud)

所以看起来价值正在错误的时间进行评估.但是,当我首先将tq转储到变量中并将该赋值移到延迟函数之外时,将测试函数更改为:

func (t *tss) test() {
    if true {
        qtmp := t.q
        defer func() {
            //assigning qtmp here still assigns 1 back to t.q
            t.q=qtmp
            fmt.Println("assigned",t.q,"to t.q")
        }()
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v\n", t.q, t.m)
}
Run Code Online (Sandbox Code Playgroud)

我得到了上面的预期输出,其中分配了50.

我是否绊倒了一个错误,或者是否有一些关于在延迟函数中分配我缺少的值的事情?

可能值得注意的是,如果我将tq in作为函数参数传递,那么它也可以:

func (t *tss) test() {
    if true {
        defer func(q int) {
            t.q=q
            fmt.Println("assigned",t.q,"to t.q")
        }(t.q)
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v\n", t.q, t.m)
}
Run Code Online (Sandbox Code Playgroud)

编辑:这是使用答案中的方法的完整版本:

type tss struct {
    q int
    m int
}

func (t *tss) test() {
    if true {
        defer func(q int) {
            t.q=q     //this will correctly evaluate to 't.q = 50'
            fmt.Println("assigned",t.q,"to t.q")
        }(t.q)
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v\n", t.q, t.m)
}

func main() {
    ts := tss{50,1}
    fmt.Printf("q=%v, m=%v\n", ts.q, ts.m)
    ts.test()
    fmt.Printf("q=%v, m=%v\n", ts.q, ts.m)
}
Run Code Online (Sandbox Code Playgroud)

和正确的输出:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1
Run Code Online (Sandbox Code Playgroud)

Mik*_*SAT 7

所以我在校对我的帖子时回答了我自己的问题.而不是删除它以隐藏我的尴尬,我会留下它,以防其他人遭受同样的困惑.

延迟函数在调用时计算函数的任何ARGUMENTS.它不会立即评估函数体中的任何值.因此,当发生延迟操作时,将执行内部分配.

所以:

  1. 代码运行
  2. 遇到延迟声明
  3. golang存储参数值以供日后使用
  4. 延迟的func的主体完全被忽略了
  5. 其余的代码运行到封闭的func的末尾
  6. 使用存储的参数值执行延迟函数

-麦克风

  • 未来注意事项:请不要在帖子上签名.每个帖子下方都有一个框,其中显示了您的用户名,因此不需要签名(并且不鼓励任何形式的问候). (2认同)