What is the difference between named return value and normal return value? - go

My question is about the different named return value vs normal return value.
my code
package main
import "fmt"
func main() {
f := fmt.Println
f(a())
f(b())
}
func a() int {
i := 0
defer func() {
i += 1
fmt.Println("a defer : ", i)
}()
return i
}
func b() (i int) {
i = 0
defer func() {
i += 1
fmt.Println("b defer : ", i)
}()
return i
}
the result:
the a function return 0
the b function reutrn 1
Why?

The named return value also allocates a variable for the scope of your function.
func a() int: While you already return the value of i = 0, but since no named values was defined the static value got returned. So even though you're increasing i in the deferred function it doesn't affect the returned value.
func b() (i int): The variable i is allocated (and already initialized to 0). Even though the deferred function runs after the i = 0 was returned the scope is still accessible and thus still can be changed.
Another point of view: you can still change named return values in deferred functions, but cannot change regular return values.
This especially holds true in the following example:
func c() (i int) {
defer func() {
i = 1
fmt.Println("c defer : ", i)
}()
return 0
}

defer runs a function after the return statement but before the function is auctually returned, thus enabling modify returned results. However, only named return results can be accessed normally, i.e. by the variable name.
The return statement, when not naked (another thing about named return, but irrelevant here), the expression got evaluated. And if the return is named, the named variable is assigned with the evaluated value.
In your code, in func a() int the return is typed but not named. So when return i is execuated, it sets the return value, a variable not available to the code, as the value of i. You can consider it as RETVAL := i. And later, your deferred function modified i but the return value (RETVAL) remains unchanged.
But in func b() (i int), i is a named return. Thus, when return i execuate, it literally translate to i = i. And later, your deffered function modified i, a return value, so the returned value change.
More on return: https://golang.org/ref/spec#Return_statements

With the named return value you directly modify what gets returned, with the "normal" return value you just modify local variable in the scope of your function, which never gets returned.
More on this:
Deferred function can access named return values but it has no return value itself - so this is actually the only way to modify main function results from there. Very useful thing.
Imagine you want to fix code which panics - you want it to return the error instead. You can solve it by using recover in a deferred function and then assigning recovered error to named return value.
Example, somewhat abstract but hopefully useful:
func noMorePanics() (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
potentiallyPanickingFunction()
}

Related

Why does go panic recover to return value with local variable not work?

This panic recover code works with named return values.
func main() {
result, err := foo()
fmt.Println("result:", result)
if err != nil {
fmt.Println("err:", err)
}
}
func foo() (result int, err error) {
defer func() {
if e := recover(); e != nil {
result = -1
err = errors.New(e.(string))
}
}()
bar()
result = 100
err = nil
return
}
func bar() {
panic("panic happened")
}
Output
result: -1
err: panic happened
But why this code with local variables does not work?
func main() {
result, err := foo()
fmt.Println("result:", result)
if err != nil {
fmt.Println("err:", err)
}
}
func foo() (int, error) {
var result int
var err error
defer func() {
if e := recover(); e != nil {
result = -1
err = errors.New(e.(string))
}
}()
bar()
result = 100
err = nil
return result, err
}
func bar() {
panic("panic happened")
}
Output
result: 0
Any explanation to help me understanding the reason / basic concept of it? In the go tour basics the explanation is as followed.
Named return values
Go's return values may be named. If so, they are treated as variables defined at the top of the function.
So it should be the same, right?
Note that this has nothing to do with panic/recover, it is a feature of the defer statement.
... 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. If the deferred function has
any return values, they are discarded when the function completes.
Spec: Return statements details this:
There are three ways to return values from a function with a result type:
The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.
The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.
The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.
So basically if you use a return statement that explicitly lists the return values, those will be used, regardless if the result parameters are named or not.
If the result parameters are named, they act as ordinary local variables: you can read and write them. If the result parameters are named, you may use a "naked" return statement, without listing the values to return. If you do so, then the actual return values will be the values of the (named) result parameters. The same thing applies if your function does not reach a return statement due to panicing and recovering: once the deferred functions run, the actual return values will be the values of the named result parameters (which the deferred functions can change and "have a say" in what to return).
If you don't use named result parameters but you declare local variables, they are not special in this way: when the function returns, those are not used "automatically" as the result values (like they would be if they would be named result parameters and not local variables). So if you change them in a deferred function, that will not have any effect on the actual values returned. In fact, if you don't use named result parameters and your function panics and recovers, you can't specify the return values, they will be the zero values of the result types. That's why you see result: 0 (0 is the zero value for int) and no error (because error is an interface type and zero value for interface types is nil and you don't print the error if it's nil).
See related: How to return a value in a Go function that panics?
Might be a brief summary for #icza's anwser:
Named return variables use their final values for returning when the function teminate with no panic(return normally or recover from panic), so you can change them in defer recover func(), and the final values changed, so be the return values.
If use local variables, compiler can not know these local variables will be used as return variables until a normal return. Local variables might be changed in panic recover, but
the return statement has not been executed yet because the panic, so the local variables you defined was not treated as return variables, the return values will be the zero values of the return types.

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

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 to nil—deferred functions can modify named return values.
See related question: How to return a value in a Go function that panics?

return value in function when using defer

I have some problem with using 'defer' about the return value. I tried to run one test function in different way (only the definition of i is different), but the result is different. So, I'm confused about the different return value. Here is the problem:
function 1:
package main
import "fmt"
func main() {
fmt.Println("a return:", a()) // return value: 0
}
func a() int {
var i int
defer func() {
i++
fmt.Println("a defer1:", i) // print " a defer1: 1"
}()
return i
}
return value:
a defer1: 1
a return: 0
function 2:
package main
import "fmt"
func main() {
fmt.Println("a return:", a()) // return value: 1
}
func a() (i int) {
defer func() {
i++
fmt.Println("a defer1:", i) // print " a defer1: 1"
}()
return i
}
return value:
a defer1: 1
a return: 1
One of the return values is 0, the other is 1. So, the question is what's the difference between the two function.
what's the difference between the two functions?
The Go Programming Language Specification
Defer statements
A "defer" statement invokes a function whose execution is deferred to
the moment the surrounding function returns.
Each time a "defer" statement executes, the function value and
parameters to the call are evaluated as usual and saved anew but the
actual function is not invoked. Instead, deferred functions are
invoked immediately before the surrounding function returns, in the
reverse order they were deferred.
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. If the deferred function has any
return values, they are discarded when the function completes.
The difference is a surrounding function with a named result parameter
func a() (i int)
versus a surrounding function with an unnamed result parameter
func a() int

Go closure variable scope

I'm reading 'CreateSpace An Introduction to Programming in Go 2012'
and on page 86 I found this evil magic
func makeEvenGenerator() func() uint {
i := uint(0)
return func() (ret uint) {
ret = i
i += 2
return
}
}
// here's how it's called
nextEven := makeEvenGenerator()
fmt.Println(nextEven())
fmt.Println(nextEven())
fmt.Println(nextEven())
1) Why is i not resetting ?
2) is nextEven() returning and uint or is Println so smart that it can work with everything ?
For the sake of clarity, I'll assign names to both functions:
func makeEvenGenerator() func() uint { // call this "the factory"
i := uint(0)
return func() (ret uint) { // call this "the closure"
ret = i
i += 2
return
}
}
The factory returns the closure – functions are first class citizens in Go i.e. they can be right-hand expressions, for example:
f := func() { fmt.Println("f was called"); }
f() // prints "f was called"
In your code, the closure wraps over the context of the factory, this is called lexical scoping. This is why the variable i is available inside the closure, not as a copy but as a reference to i itself.
The closure uses a named return value called ret. What this means is that inside the closure you'll have implicitly declared ret and at the point of return, whatever value ret has will be returned.
This line:
ret = i
will assign the current value of i to ret. It will not change i. However, this line:
i += 2
will change the value of i for the next time the closure is called.
Here you'll find a little closure example I wrote together for you. It's not extremely useful but illustrates the scope, purpose and use of closures pretty well in my opinion:
package main
import "fmt"
func makeIterator(s []string) func() func() string {
i := 0
return func() func() string {
if i == len(s) {
return nil
}
j := i
i++
return func() string {
return s[j]
}
}
}
func main() {
i := makeIterator([]string{"hello", "world", "this", "is", "dog"})
for c := i(); c != nil; c = i() {
fmt.Println(c())
}
}
1) Why is i not resetting ?
Closures in Go capture variables by reference. That means the inner function holds a reference to the i variable in the outer scope, and each call of it accesses this same variable.
2) is nextEven() returning and uint or is Println so smart that it can
work with everything ?
fmt.Println() (along with fmt.Print(), fmt.Fprint(), etc.) can work most types. It prints its arguments in the "default format". It is the same thing that is printed using fmt.Printf() using the %v verb.
The variable in closure is free from neither code segment nor context.

Why would return parameters be named?

What benefits arise from naming a function's return parameter(s)?
func namedReturn(i int) (ret int) {
ret = i
i += 2
return
}
func anonReturn(i int) int {
ret := i
i += 2
return ret
}
There are some benefits to naming them:
It serves as documentation.
They are auto-declared and initialized to the zero values.
If you have multiple return sites, you don't need to change them all if you change the function's return values since it will just say "return".
There are also downsides, mainly that it's easy to accidentally shadow them by declaring a variable of the same name.
Effective Go has a section on named result parameters:
The return or result "parameters" of a Go function can be given names
and used as regular variables, just like the incoming parameters. When
named, they are initialized to the zero values for their types when
the function begins; if the function executes a return statement with
no arguments, the current values of the result parameters are used as
the returned values.
The names are not mandatory but they can make code shorter and
clearer: they're documentation. If we name the results of nextInt it
becomes obvious which returned int is which.
func nextInt(b []byte, pos int) (value, nextPos int) {
[...]
Another special use for a named return variable is to be captured by a deferred function literal. A trivial illustration:
package main
import (
"errors"
"fmt"
)
func main() {
fmt.Println(f())
}
var harmlessError = errors.New("you should worry!")
func f() (err error) {
defer func() {
if err == harmlessError {
err = nil
}
}()
return harmlessError
}
Output is <nil>. In more practical scenarios, the deferred function may handle panics, and may modify other return values besides an error result. The magic in common though, is that the deferred literal has a chance to modify the return values of f after f is terminated, either normally or by panic.
It's useful in at least two cases:
Whenever you have to declare variables that you're going to return. E.g.
func someFunc() (int, error) {
var r int
var e error
ok := someOtherFunc(&r) // contrived, I admit
if !ok {
return r, someError()
}
return r, nil
}
vs.
func someFunc() (r int, e error) {
ok := someOtherFunc(&r)
if !ok {
e = someError()
}
return
}
This gets more important as the number of execution paths through the function increases.
When you're documenting return values and want to refer to them by name. godoc considers the return variables part of a function's signature.
For example, named return parameters are accessible by, well, name.
func foo() (a, b, c T) {
// ...
if qux {
b = bar()
}
// ...
return
}
This is not easy to replicate w/o named return parameters. One would have to introduce local variables of essentially the same functionality as named return parameters:
func foo() (T, T, T) {
var a, b, c T
// ...
if qux {
b = bar()
}
// ...
return a, b, c
}
So it's easier to allow that directly.
Additionally, they are accessible also in the other direction:
func foo() (a, b, c T) {
// ...
if a > c {
b = bar()
}
// ...
return
}
Etc.

Resources