defer in a loop

5,803 views
Skip to first unread message

Matt Kane's Brain

unread,
Apr 23, 2012, 9:45:38 AM4/23/12
to golang-nuts
Say I have a loop that is something like this:

for _, name := range filenames {
f, err := os.Open(name)
if err != nil { panic(err) }
defer f.Close()
// do some stuff
}

If the list of filenames is really big, I'll end up with a whole bunch
of open files until the function that contains the loop returns. (and
if it's a goroutine it might never return!) But I like to think that
the habit of using defer to close resources near where they are
acquired is a good one. Is it overkill to do something like this?

for _, name := range filenames {
func() {
f, err := os.Open(name)
if err != nil { panic(err) }
defer f.Close()
// do some stuff
}()
}

Or is defer not the right thing to do in a case like this?

--
matt kane's brain
http://hydrogenproject.com

Patrick Mylund Nielsen

unread,
Apr 23, 2012, 9:50:52 AM4/23/12
to Matt Kane's Brain, golang-nuts
IMO calling f.Close immediately after doing the stuff is preferrable
in this case, and any other case where it is likely that a significant
amount of time will pass between the work with the file is completed,
and the function returns.

Vanja Pejovic

unread,
Apr 23, 2012, 9:51:47 AM4/23/12
to Matt Kane's Brain, golang-nuts

Why not make it a named function instead of a closure? I don't think it's overkill.

sdeg...@8thlight.com

unread,
Apr 23, 2012, 10:14:45 AM4/23/12
to golan...@googlegroups.com
Often it turns out that "do some stuff" is a significant (not to be confused with large) task and can be its own function, which the loop just calls. A bonus in that case is that you can use defer as normal.

-Steven

Anthony Martin

unread,
Apr 23, 2012, 10:39:41 AM4/23/12
to Vanja Pejovic, Matt Kane's Brain, golang-nuts
Vanja Pejovic <vva...@gmail.com> once said:
> Why not make it a named function instead of a closure? I don't think it's
> overkill.

There's no runtime overhead for closures that
are called in place or for function literals
that don't close over any variables. They both
behave exactly like any other named function.

Anthony

Vanja Pejovic

unread,
Apr 23, 2012, 10:42:26 AM4/23/12
to Anthony Martin, Matt Kane's Brain, golang-nuts
Oh, that's good to know. Thanks.

André Moraes

unread,
Apr 23, 2012, 12:00:43 PM4/23/12
to Matt Kane's Brain, golang-nuts
>
> Or is defer not the right thing to do in a case like this?

Defer is the only guarantee that you can have if something wrong
happens, so use it.

By letting the file.Close() for a later place in the code you add a
possibility that on a later change in your code the Close() call isn't
executed.

In your case, since you had a lot of open files, you should start a
goroutine with your file name and open the file there.

package main

import "sync"

func handleFile(name string, wg *sync.WaitGroup ) {
// make sure to signal when everything is done
defer wg.Done()

f, err := os.Open(name)

if err != nil {

return
}

// use f
defer f.Close()
}

func doLotsOfWork() {
done := &sync.WaitGroup{}
fileCount := 0



for _, name := range filenames {

done.Add(1)
go handleFile(name,done)
}

// block until everything is done
done.Wait()
}

>
> --
> matt kane's brain
> http://hydrogenproject.com

--
André Moraes
http://andredevchannel.blogspot.com/

André Moraes

unread,
Apr 23, 2012, 12:01:55 PM4/23/12
to Matt Kane's Brain, golang-nuts
>
>        // use f
>        defer f.Close()

oops, the right way is:

defer f.Close()
// use f

Ugorji Nwoke

unread,
Apr 23, 2012, 12:12:26 PM4/23/12
to golan...@googlegroups.com, Matt Kane's Brain
IMO, I think a goroutine may be heavy handed here. If the OP just wants to ensure a file is eventually closed, and wants it to happen within each iteration of the loop, then the content of each loop should be run in a function (externally defined or closure as he has it) and use defer as usual.

Matt Kane's Brain

unread,
Apr 23, 2012, 12:18:42 PM4/23/12
to Ugorji Nwoke, golan...@googlegroups.com
Actually in my case a goroutine would be terrible as I need to read
the contents of each file in the specified order! But moving it to a
named function is probably reasonable.

The real useful part though is:


> There's no runtime overhead for closures that
> are called in place or for function literals
> that don't close over any variables


On Mon, Apr 23, 2012 at 12:12, Ugorji Nwoke <ugo...@gmail.com> wrote:
> IMO, I think a goroutine may be heavy handed here. If the OP just wants to
> ensure a file is eventually closed, and wants it to happen within each
> iteration of the loop, then the content of each loop should be run in a
> function (externally defined or closure as he has it) and use defer as
> usual.

--

Rémy Oudompheng

unread,
Apr 23, 2012, 1:42:45 PM4/23/12
to Matt Kane's Brain, golang-nuts
Le 23 avril 2012 15:45, Matt Kane's Brain
<mkb-...@hydrogenproject.com> a écrit :

> Say I have a loop that is something like this:
>
> for _, name := range filenames {
>    f, err := os.Open(name)
>    if err != nil { panic(err) }
>    defer f.Close()
>    // do some stuff
> }
>
> If the list of filenames is really big, I'll end up with a whole bunch
> of open files until the function that contains the loop returns.

I think you can write:

for _, name := range filenames {
f, err := os.Open(name)
if err != nil { panic(err) }
defer f.Close()
// do some stuff

f.Close()
}

Rémy.

Paul Ruane

unread,
Apr 24, 2012, 4:09:47 AM4/24/12
to golan...@googlegroups.com
On Monday, April 23, 2012 2:45:38 PM UTC+1, mkb wrote:
Say I have a loop that is something like this:

for _, name := range filenames {
    f, err := os.Open(name)
    if err != nil { panic(err) }
    defer f.Close()
    // do some stuff
}

I was going to suggest having an extra scoping block so that the defer runs at the end of that instead of the end of the loop:

    for _, name := range filenames {
        {
            f, err := os.Open(name)
            if err != nil { panic(err) }
            defer f.Close()
            // do some stuff
        }
        // I expected the defer to run here
    }
    // the defer actually runs here

But this doesn't work. Anyone know why defer does not run at the end of the scoping block? Is this by design?

Dave Cheney

unread,
Apr 24, 2012, 4:50:07 AM4/24/12
to Paul Ruane, golan...@googlegroups.com
defer statements are evaluated just before the function returns to the caller.

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

Cheers

Dave

Julien Laffaye

unread,
Apr 24, 2012, 4:54:07 AM4/24/12
to Dave Cheney, Paul Ruane, golan...@googlegroups.com
I've come to appreciate how it forces me to write more named functions,
with fewer lines of code in each one.
In fact it forces me to layer the code!

Paul Ruane

unread,
Apr 24, 2012, 5:05:47 AM4/24/12
to golan...@googlegroups.com, Paul Ruane
Thanks, I'll have to revisit my code -- I fear I have a number of defers running later than I thought.


On Tuesday, April 24, 2012 9:50:07 AM UTC+1, Dave Cheney wrote:
defer statements are evaluated just before the function returns to the caller.

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

Cheers

Dave

Rémy Oudompheng

unread,
Apr 24, 2012, 7:07:19 AM4/24/12
to Paul Ruane, golang-nuts
defer runs at the end of the function by design. How would you defer
something under a condition?

If you need to do something at the end of a block, do it at the end of
the block. If you need to collect dangling resources on a panic, use
defer.

If you want both, use both. If you want the action to be done once,
use sync.Once.

Rémy.

Unknown

unread,
Apr 24, 2012, 10:07:49 AM4/24/12
to golan...@googlegroups.com
From my experience its safe to call Close multiple times, I use that
pattern all the time to make sure that database connections get closed
asap (and to make sure that if I somehow miss closing them, they get
closed at the end of the function)

roger peppe

unread,
Apr 24, 2012, 10:29:26 AM4/24/12
to Unknown, golan...@googlegroups.com

it's a pity we can't close a channel twice.

Unknown

unread,
Apr 24, 2012, 10:31:04 AM4/24/12
to roger peppe, golan...@googlegroups.com
Well, technically you can, you just won't like the result of doing so :D

Steven Blenkinsop

unread,
Apr 24, 2012, 8:18:59 PM4/24/12
to Unknown, golan...@googlegroups.com
On Tue, Apr 24, 2012 at 10:07 AM, Unknown <plague...@gmail.com> wrote:
From my experience its safe to call Close multiple times, I use that
pattern all the time to make sure that database connections get closed
asap (and to make sure that if I somehow miss closing them, they get
closed at the end of the function)

This'll break as soon as you have a "continue"  statement somewhere in your loop. It would probably be best just to use a function to explicitly scope the defer.
Reply all
Reply to author
Forward
0 new messages