s/puts them/puts the result/ On Thu, Jan 11, 2018 at 8:57 AM, Axel Wagner <axel.wagner...@googlemail.com> wrote:
> On Thu, Jan 11, 2018 at 8:10 AM, 'Eric Johnson' via golang-nuts < > golang-nuts@googlegroups.com> wrote: > >> 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. >> > > There are basically two reasons > <https://github.com/golang/go/wiki/CodeReviewComments#named-result-parameters> > to use named return values: 1) Documentation, 2) Do what you are doing here. > It is interesting, though, that you find it confusing. To me, it always > seemed kind of natural, that a defered function executes *after* the > return. If you accept that part, the behavior should be very clear, IMO. > > I looked through the language spec, and did not see this clearly spelled >> out. >> > > It's spelled out here <https://golang.org/ref/spec#Defer_statements>, > that defered functions can modify named returns. Though you are right that > it's not explicitly spelled out whether they execute before, after or > concurrent with the return statement. > > >> 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 don't agree. Currently, the sequence of a return is > > 1. The expressions in the return statement are evaluated and stored as > return values (named or unnamed) > 2. All defered functions are executed (potentially modifying the named > returns) > 3. Control is returned > > What you'd want amounts to > > 1. All defered functions are executed (potentially modifying the named > returns) > 2. All expressions in the return statement are evaluated and stored as > return values > 3. Control is returned > > Not only do I not see how this would be simpler to explain - it also opens > up some other problems. > For example, when I use named returns and modify them, I usually do it in this > pattern <https://play.golang.org/p/iERVXiy5Yq7>. In the changed sequence > of events, this would return "closing failed", instead of "stuff failed". > And how would this <https://play.golang.org/p/VDt3A0lnFN7> work? recover > can only work, after the returned expressions where evaluated; but the > defer executes before that. > > The alternative would be, to somehow make return not actually return > values, but to have something like > 1. The expressions in the return statement are evaluated as references > 2. All defered functions are executed > 3. The references from the return statement are dereferenced and the > values stored as return values > 4. Control is returned > But for that to work, you'd need to only allow addressable expressions in > return (which would be bad) or you would need to somehow introduce a new, > weird way to describe the sequence of events. Either way, this doesn't seem > simpler to explain, yet again. > > >> 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. >> > > I agree that there is *some* subtlety here, but I also believe it's easier > to explain than the alternatives: > 1) Return values are stored somewhere that isn't a local variable. You can > name that storage, or don't, but it always exists > 2) with := you are declaring a new, local variable. > 3) return evaluates its expressions and puts them into the return storage > 4) defered functions are executed after return, but before control is > returned/panic'ing continues. > > Seems doable. > > >> 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. >> > > -- 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.