BlackMamba
BlackMamba

Reputation: 10254

How to understand `defer` in go language?

In the godoc(https://blog.golang.org/defer-panic-and-recover), there is an example:

  1. Deferred functions may read and assign to the returning function's named return values.

In this example, a deferred function increments the return value i after the surrounding function returns. Thus, this function returns 2:

func c() (i int) {
    defer func() { i++ }()
    return i
}

I also wrote a small progam:

package main

import "fmt"

func b() int {
    i := 0
    for ; i < 4; i++ {
        defer func() {fmt.Println(i); i++} ()
    }
    return i
}
func main() {
  fmt.Println("result = ", b())
}

the output is:

4
5
6
7
result =  4

So I am confused, why does the second example not output 8 ?

Upvotes: 5

Views: 1534

Answers (3)

jackbean818
jackbean818

Reputation: 314

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Your func b() is semantically equivalent to:

func b() int {
    i := 0
    for ; i < 4; i++ {
    }
    ret := i
    fmt.Println(i); i++
    fmt.Println(i); i++
    fmt.Println(i); i++
    fmt.Println(i); i++
    return ret
}

Ergo b() returns 4.

Upvotes: 0

Blixt
Blixt

Reputation: 50169

Note the part that says "may read and assign to the returning function's named return values."

This means that:

func b() int {
    var i int
    defer func() { fmt.Println(i); i++ }()
    return i
}

will say 0 and result = 0, while:

func b() (i int) {
    defer func() { fmt.Println(i); i++ }()
    return i
}

will say 0 and result = 1.

It might help to imagine that the return i in my first example assigns the i value to a hidden return variable (because it's not named), then goes on to execute the defer statements (which only modify the local variable i), while in the second example we assign directly to the return variable (because it's named) and so the defer statement is able to change it.

Basically your program could be interpreted like this:

package main

import "fmt"

func b() (hiddenVariable int) {
    i := 0
    for ; i < 4; i++ {
        defer func() { fmt.Println(i); i++ }()
    }
    hiddenVariable = i; return // implicit meaning of return i
}

func main() {
    fmt.Println("result = ", b())
}

Upvotes: 10

PC Princess
PC Princess

Reputation: 1

Based solely on what I learned about defer and after looking at your code, I'd like to say that the for loop defers the print out of subsequent 'i's until later, however, the for loop still runs and therefore affects the 'i' returned by func b(), which is 4.

for ; i < 4; i++ {
    defer func() {fmt.Println(i); i++} ()
}
return i

Because the print statement and it's 'value' for 'i' is deferred, they increment beyond 4, but the 'i' after the for loop remains at 4.

Upvotes: 0

Related Questions