Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I get 0 and 1 in the following golang code example with defer

Tags:

go

deferred

Call to defer produces different results for variables declared in two different ways

package main

import (
    "fmt"
)

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

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

func c2() (i int) {
    defer func() { i++ }()
    return 2
}

func main() {
    fmt.Println(c(0)) // Prints 0
    fmt.Println(c1()) // Prints 1
    fmt.Println(c2()) // Prints 3 Thank you icza
}

https://play.golang.org/p/gfnnCZ--DkH

like image 444
Larytet Avatar asked May 02 '19 10:05

Larytet


1 Answers

In the first example i is an (incoming) parameter. At the return statement the return value is evaluated, and the deferred function runs after this, and incrementing i in that has no effect on the return value.

In the second example i is the name of the result parameter. At the return statement you explicitly return the value i, which is then assigned to the return value i (this is a no-op). But deferred functions are allowed to modify the values of the return "variables", and if they do so, that will have an effect on the actual returned values.

This becomes clearer if we add another example:

func c2() (i int) {
    defer func() { i++ }()
    return 2
}

This function will return 3, because the return 2 statement will assign 2 to i, then the deferred function will increment this, and so the return value will be 3. Try this one on the Go Playground. Relevant part from the Spec: Return statements:

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

In general, if a function (or method) has named result parameters, the return values will always be the values of those variables, but must not forget that a return statement may assign new values to these result paramteters, and they may be modified by deferred functions after a return statement.

This is mentioned in the Spec: Defer statements:

For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.

It is also mentioned in the blog post Defer, Panic and Recover:

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

And also in Effective Go: Recover:

If doParse panics, the recovery block will set the return value to nil—deferred functions can modify named return values.

See related question: How to return a value in a Go function that panics?

like image 168
icza Avatar answered Oct 21 '22 14:10

icza