I thought named returns was just a code readability feature that declares and initializes returns for you, but it seems there's more to it. What am I missing?
If you name the result parameters, their actual value at the time of returning to the caller will determine the returned values. Meaning you can change their values like other local variables, and if the expression list of the return
statement is empty, their last assigned values will be used. Also if there are deferred functions, they can modify the values of the named result parameters after the return
statement and before the function returns to its caller, and those modifications will be preserved. It also allows to modify return values in case of a panic, see How to return a value in a Go function that panics?
Spec: Return statements:
Regardless of how they [the return values] are declared, all the result values are initialized to the zero values for their type upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.
And 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.
In NormalReturns()
: The return value is initialized to its zero value (which is nil
for all interface types, including the builtin error
type), and since the return
statement is not reached (due to a panic in panicIf42()
), it will stay nil
. It doesn't matter if the local variable err
is changed, that is not the result variable. It's just an ordinary variable. It will have no effect on the value returned
In general, if a function does not have named result variables, and if this function does not reach a return
statement (e.g. due to a panic), it cannot have return values other than (meaning different from) the zero values of the result types.
In NamedReturns()
the deferred catch()
will modify the named result variable err
. The changes are "preserved": whatever the named result variables hold will be returned when the function ends (which happens after calling deferred functions, if there are any). So even though the return
statement is not reached here either, the catch()
function changes the err
result variable, and whatever is assigned to it will be used as the value returned.
More on the topic:
Go blog: 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.