Defer, function literals, and named result parameters

293 views
Skip to first unread message

Dato Simó

unread,
Mar 20, 2014, 8:01:46 PM3/20/14
to golan...@googlegroups.com
Hello—

I’m getting started with Go, and I found something really odd with the defer
statement. (See code snippet below.)

In a nutshell: are named result parameters modified in a defer’d function
literal supposed to _completely override_ the existing return statement; that
is, even if a literal, and not the named result, is being returned?

Consider the following complete Go program:

-8<-
package main

import (
"fmt"
)

func main() {
fmt.Printf("Value returned is: %d\n", testDefer())
}

func testDefer() (value int) {
defer func() {
value = 2
}()

value = 1
return 3
}
->8-

I would expect testDefer() to return 3, because there’s no bare return, and a
literal 3, not “value”, is returned. However, it returns 2.

% go version
go version go1.2.1 darwin/amd64

% go run defer.go
Value returned is: 2

The language spec briefly mentions the interaction of defer and named result
parameters [1], but is silent on the above. Is this intended behavior?

Thanks.


[1] “For instance, if the deferred function is a function literal and the
surrounding function has named result parameters that are in scope within
theliteral, the deferred function may access and modify the result
parameters before they are returned.”

http://golang.org/ref/spec#Defer_statements

Jesse McNelis

unread,
Mar 20, 2014, 8:12:57 PM3/20/14
to Dato Simó, golang-nuts
On Fri, Mar 21, 2014 at 11:01 AM, Dato Simó <da...@net.com.org.es> wrote:
Hello—

I’m getting started with Go, and I found something really odd with the defer
statement. (See code snippet below.)

In a nutshell: are named result parameters modified in a defer’d function
literal supposed to _completely override_ the existing return statement; that
is, even if a literal, and not the named result, is being returned?

"Regardless of how they 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."



 
--
=====================
http://jessta.id.au

Caleb Spare

unread,
Mar 20, 2014, 8:14:04 PM3/20/14
to Dato Simó, golang-nuts
Note that the code example in the spec immediately below this quote
shows that the behavior you discovered is as intended.

>
> --
> 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...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Dato Simó

unread,
Mar 20, 2014, 8:21:42 PM3/20/14
to Caleb Spare, golang-nuts

On Fri, Mar 21, 2014 at 12:14 AM, Caleb Spare <ces...@gmail.com> wrote:
Note that the code example in the spec immediately below this quote
shows that the behavior you discovered is as intended.

​Whoops, thanks.

The really interesting result is that:

func f() (result int) {
defer func() {
result++
}()
return 7
}

​returns 8 (not 1). Fair enough, then. (Which is the bit of spec that Jesse pointed to, but I didn't understand at first.)

Thanks again.​

Jesse McNelis

unread,
Mar 20, 2014, 8:34:56 PM3/20/14
to Dato Simó, Caleb Spare, golang-nuts
On Fri, Mar 21, 2014 at 11:21 AM, Dato Simó <da...@net.com.org.es> wrote:
The really interesting result is that:

func f() (result int) {
defer func() {
result++
}()
return 7
}

​returns 8 (not 1). Fair enough, then. (Which is the bit of spec that Jesse pointed to, but I didn't understand at first.)

That's not all the interesting.
 
func f() (int) {
       result := 0
defer func() {
result++
}()
        result = 7
return result
}

does the same thing.

roger peppe

unread,
Mar 21, 2014, 6:17:26 AM3/21/14
to Dato Simó, Caleb Spare, golang-nuts
This behaviour is the basis of a useful idiom that I've used a fair few times.
Sometimes you want to perform some cleanup of some resources only
if there's an error.

For example, this will correctly remove the temporary file
when an error occurs, but leave it intact otherwise:

func openDataFile() (_ *os.File, err error) {
f, err := io.TempFile("", "")
if err != nil {
return nil, err
}
defer func() {
if err != nil {
os.Remove(f.Name())
}
}()
if err := fillFileFromNetwork(f); err != nil {
return err
}
if err := verifyFile(f); err != nil {
return err
}
return f, nil
}

Kyle Lemons

unread,
Mar 21, 2014, 9:06:41 PM3/21/14
to Jesse McNelis, Dato Simó, Caleb Spare, golang-nuts

atomly

unread,
Mar 21, 2014, 10:58:14 PM3/21/14
to Kyle Lemons, Jesse McNelis, Dato Simó, Caleb Spare, golang-nuts
As is expected:

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. If the deferred function has any return values, they are discarded when the function completes. (See also the section on handling panics.)
 

:: atomly ::

[ ato...@atomly.com : www.atomly.com  : http://blog.atomly.com/ ...
[ atomiq records : new york city : +1.347.692.8661 ...
[ e-mail atomly-new...@atomly.com for atomly info and updates ...
Reply all
Reply to author
Forward
0 new messages