golang推迟不评估何时预期

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 't.q = 50' right?
            fmt.Println("assigned",t.q,"to t.q")
        }()
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v
", t.q, t.m)
}

func main() {
    ts := tss{50,1}
    fmt.Printf("q=%v, m=%v
", ts.q, ts.m)
    ts.test()
    fmt.Printf("q=%v, m=%v
", 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("assigned",t.q,"to t.q")
        }()
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v
", 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("assigned",t.q,"to t.q")
        }(t.q)
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v
", 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 't.q = 50'
            fmt.Println("assigned",t.q,"to t.q")
        }(t.q)
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v
", t.q, t.m)
}

func main() {
    ts := tss{50,1}
    fmt.Printf("q=%v, m=%v
", ts.q, ts.m)
    ts.test()
    fmt.Printf("q=%v, m=%v
", 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

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

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 't.q = 50' right?
            fmt.Println("assigned", t.q, "to t.q")
        }(t.q)
        t.q = t.m
    }
    fmt.Printf("q=%v, m=%v
", t.q, t.m)
}

OUTPUT

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