I was looking at fixing an issue flagged by gometalinter, and realized I 
didn't understand the actual behavior of defer statements. Specifically, I 
was not handling the error return of a close operation.

I wrote a test program so I could understand the behavior, which led to 
these two function

package main

import "fmt"

func checkDefer(newVal int, val *int) {
fmt.Printf("current value is %v\n", *val)
*val = newVal
}

func main() {
fmt.Printf("variableOnUnnamedReturn value is %v, expected 30\n", 
variableOnUnnamedReturn(20, 25, 30))
fmt.Printf("variableOnNamedReturn value is %v, expected 45\n", 
variableOnNamedReturn(35, 40, 45))
}

func variableOnUnnamedReturn(start, test, def int) int {
retval := start
defer checkDefer(def, &retval)
retval = test
return retval
}

func variableOnNamedReturn(start, test, def int) (retval int) {
retval = start
defer checkDefer(def, &retval)
retval = test
return retval
}


The body of the two functions differs only by a single ":", and yet, the 
output of the Printf calls differs. This appears to be due to very subtle 
differences between how named and unnamed returns are handled in the 
context of defer statements.

I looked through the language spec, and did not see this clearly spelled 
out. It is not obvious to me, a relatively experience Go developer, that 
the "variableOnUnnamedReturn" function should return a 25 in the above use, 
but the "variableOnNamedReturn" function returns a 45 in the above use.

As near as I can tell, operationally, the "unnamed" return could be 
re-written in something like the following fashion (where addDeferredOps is 
a collector for deferred operations.):

        retval := start
        dos := addDeferredOps(func() { checkDefer(def, &retval)} )
        retval = test
        _retval1_ = retval
        dos.performDeferredOps()
        return _retval1_

Which makes it obvious that the call to checkDefer() won't change the 
return of the function. Whereas, the "named return" version effectively 
amounts to this:

        retval := start
        dos := addDeferredOps(func() { checkDefer(def, &retval)} )
        retval = test
        dos.performDeferredOps()
        return retval

I read the language spec, and simply didn't see the description of the 
difference between these two behaviors. Seems mostly like a language bug, 
to me. Feels like the named and unnamed return cases should behave the 
same. I can see why they don't (expressions on the return line, instead of 
simple uses of the variables), but that doesn't make me happy about having 
to explain the difference to others on my team.

Eric.



-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to