golang中的defer在预期时不会被评估。

huangapple go评论82阅读模式
英文:

golang defer not evaluating when expected

问题

根据规范,defer函数中的值在调用defer函数时进行求值,但是这些操作直到封闭函数返回时才执行。我理解了这一点,并且理解了整个“for i:=0;i<4;i++ defer example prints 3210”的情况。

然而,当我尝试使用defer临时赋予一个覆盖值(将最大值m赋给队列长度q),然后确保在完成后重置该值时(为了演示简化了示例):

type tss struct {
    q int
    m int
}

func (t *tss) test() {
    if true {
        defer func() {
            t.q = t.q //这应该等于't.q = 50',对吗?
            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)
}

我期望得到以下输出:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1

但实际输出是:

q=50, m=1
q=1, m=1
assigned 1 to t.q
q=1, m=1

因此,似乎值在错误的时间进行了求值。然而,当我首先将t.q转储到一个变量中,并将该赋值移出延迟函数,并将test函数更改为以下形式:

func (t *tss) test() {
    if true {
        qtmp := t.q
        defer func() {
            //在这里分配qtmp仍然会将1分配回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)
}

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

我是不是触发了一个错误,或者在延迟函数中赋值有什么我忽略的问题?

可能值得注意的是,如果我将t.q作为函数参数传递进去,那也可以正常工作:

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)
}

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

type tss struct {
    q int
    m int
}

func (t *tss) test() {
    if true {
        defer func(q int) {
            t.q = q //这将正确地等于'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)
}

正确的输出如下:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1
英文:

So according to spec, the values in a defer function are evaluated when the defer function is called, but the actions are not executed until the enclosing function returns. I get this, and understand the whole 'for i:=0;i<4;i++ defer example prints 3210' thing.

However, when i try to use defer to temporarily assign an override value (assign max m to queue-length q), then ensure that value is reset when I'm done (example simplified for demo):

type tss struct {
    q int
    m int
}

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

func main() {
    ts := tss{50,1}
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, ts.q, ts.m)
    ts.test()
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, ts.q, ts.m)
}

I would expect to receive the output:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1

But the actual output is:

q=50, m=1
q=1, m=1
assigned 1 to t.q
q=1, m=1

So it would seem the values are being evaluated at the wrong time. However, when I dump t.q into a variable first and move that assignment outside the deferred function, and change the test function to look like:

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(&quot;assigned&quot;,t.q,&quot;to t.q&quot;)
        }()
        t.q = t.m
    }
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, t.q, t.m)
}

I get the expected output above, where 50 is assigned.

Did I trip a bug, or is there something about assigning a value within the deferred func that I'm missing?

Possibly of note, if I pass t.q in as a function argument, that works as well:

func (t *tss) test() {
    if true {
        defer func(q int) {
            t.q=q
            fmt.Println(&quot;assigned&quot;,t.q,&quot;to t.q&quot;)
        }(t.q)
        t.q = t.m
    }
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, t.q, t.m)
}

EDIT: here is the full version using the method in the answer:

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 &#39;t.q = 50&#39;
            fmt.Println(&quot;assigned&quot;,t.q,&quot;to t.q&quot;)
        }(t.q)
        t.q = t.m
    }
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, t.q, t.m)
}

func main() {
    ts := tss{50,1}
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, ts.q, ts.m)
    ts.test()
    fmt.Printf(&quot;q=%v, m=%v\n&quot;, ts.q, ts.m)
}

and the correct output:

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1

答案1

得分: 10

所以,在校对我的帖子时,我回答了自己的问题。我不会删除它来掩饰我的尴尬,我会保留它,以防其他人遇到同样的困惑。

延迟函数在调用时评估函数的任何参数。它不会立即评估函数体内的任何值。因此,内部赋值在延迟操作发生时执行。

所以:

  1. 代码被运行
  2. 遇到延迟语句
  3. Golang将参数值存储起来以备后用
  4. 延迟函数的函数体完全被忽略
  5. 运行剩余的代码直到封闭函数的末尾
  6. 使用存储的参数值执行延迟函数
  • Mike
英文:

So I answered my own question while proofreading my post. Rather than delete it to hide my embarrassment, I'll leave it up in case others suffer the same confusion.

The deferred function evaluates any ARGUMENTS to the function when it's called. It does not immediately evaluate any values WITHIN the function body. So internal assignments are executed when the deferred actions take place.

So:

  1. code is run
  2. a defer statement is encountered
  3. golang stores the argument values for later
  4. the deferred func's body is completely ignored
  5. the rest of the code is run to the end of the enclosing func
  6. the deferred function is executed using the stored argument values

-Mike

答案2

得分: 3

很好,你自己回答了问题。我认为值得展示对你最初尝试的修正,以供未来的读者参考。

func (t *tss) test() {
    if true {
        defer func(q int) {
            t.q = q //这应该评估为 '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)
}

输出

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1
英文:

Good job answering your own question. I think it is worth it to show what the correction would be to your initial attempt for future readers.

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

OUTPUT

q=50, m=1
q=1, m=1
assigned 50 to t.q
q=50, m=1

huangapple
  • 本文由 发表于 2015年2月12日 00:52:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/28459917.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定