How does defer and named return value work?

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. -- The Go Blog: Defer, Panic, and Recover

Another way to understand the above statement:

A defer statements pushes a function call onto a stack. The stack of saved calls popped out (LIFO) and deferred functions are invoked immediately before the surrounding function returns.

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

After 1 is returned, the defer func() { i++ }() gets executed. Hence, in order of executions:

  1. i = 1 (return 1)
  2. i++ (defer func pop out from stack and executed)
  3. i == 2 (final result of named variable i)

For understanding sake:

 func c() (i int) {
    defer func() { fmt.Println("third") }()
    defer func() { fmt.Println("second") }()
    defer func() { fmt.Println("first") }()

    return 1
}

Order of executions:

  1. i = 1 (return 1)
  2. "first"
  3. "second"
  4. "third"

According to the Go Specification:

Return Statements A "return" statement that specifies results sets the result parameters before any deferred functions are executed.

Defer Statements "...deferred functions are invoked immediately before the surrounding function returns..."

So yes, as you assumed, the named return variable is assigned, then the deferred statement increments it.

I would add that named return parameters can lead to subtle bugs, and generally should be avoided unless there's no alternative.


I think the confusion is about function in function how about if you classified like this:

  func main() {
      fmt.Println(c()) //the result is 5
  }

  // the c function returned value is named j
  func c() (j int)  {
      defer changei(&j)
      return 6
  }
  func changei(j *int) {
      //now j is 6 because it was assigned by return statement 
      // and if i change guess what?! i changed the returned value
      *j--;
  }

but if the return value is not named like this:

  func main() {
      fmt.Println(c()) //the result will become 6
  }

  // the c function returned value is not named at this time
  func c() int  {
      j := 1
      defer changei(&j)
      return 6
  }
  func changei(j *int) {
      //now j = 1
      // and if i change guess what?! it will not effects the returned value
      *j--;
  }

I hope this will clear the confusion and that is how i did happy Go coding