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
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 tonil
—deferred functions can modify named return values.
See related question: How to return a value in a Go function that panics?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With