defer and for loops

4,042 views
Skip to first unread message

Albert Strasheim

unread,
Oct 13, 2010, 9:45:44 AM10/13/10
to golang-nuts
Hello golangers,

According to the Go spec:

http://golang.org/doc/go_spec.html#Defer_statements

defers happen when the surrounding function returns.

I wrote code like this today:

for {
file, err := os.Open("/dev/null", os.O_RDONLY, 0)
defer file.Close()
// ...
}

which obviously didn't work, as the spec says.

I see it as "file" going out of scope at the end of the for iteration,
so it seemed plausible that a defer operating on it should run.

What's the thinking behind only doing defer on function returns, as
opposed to also doing it on for-loop "returns"?

Regards

Albert

fango

unread,
Oct 13, 2010, 10:05:05 AM10/13/10
to golang-nuts
defer happens at function return, not out of block scope. your 'file'
need to declare at function scope

fango

unread,
Oct 13, 2010, 10:06:57 AM10/13/10
to golang-nuts
Check Russ's blog on http://research.swtch.com/2010/03/broken-abstractions-in-go.html

defer is a 'magical' trick.

Cheers,
Fango

On Oct 13, 9:45 pm, Albert Strasheim <full...@gmail.com> wrote:

chris dollin

unread,
Oct 13, 2010, 10:13:07 AM10/13/10
to fango, golang-nuts
On 13 October 2010 15:05, fango <fan.h...@gmail.com> wrote:
> defer happens at function return, not out of block scope. your 'file'
> need to declare at function scope

Albert asked:

>> What's the thinking behind only doing defer on function returns, as
>> opposed to also doing it on for-loop "returns"?

Your reply doesn't answer his question, and declaring the file
at function scope wouldn't help in his example -- there are,
presumably, lots of files, one per iteration. A single defer
won't close them all.

Chris

--
Chris "allusive" Dollin

chris dollin

unread,
Oct 13, 2010, 10:14:45 AM10/13/10
to fango, golang-nuts
On 13 October 2010 15:06, fango <fan.h...@gmail.com> wrote:
> Check Russ's blog on http://research.swtch.com/2010/03/broken-abstractions-in-go.html
>
> defer is a 'magical' trick.

The /implementation/ of defer is a clever trick, but that's not its
definition, nor (I suspect) the reason that defer functions get
called on function exit.

fango

unread,
Oct 13, 2010, 10:21:13 AM10/13/10
to golang-nuts
But that's the status quo. Defer is a gift, not a over-engineered
feature. I won't ask for more than the necessities.
Anyway, any block can be put into a function call and use defer,
doesn't it?

Cheers,
Fango


On Oct 13, 10:13 pm, chris dollin <ehog.he...@googlemail.com> wrote:

roger peppe

unread,
Oct 13, 2010, 10:25:03 AM10/13/10
to chris dollin, fango, golang-nuts
the non-scope-defined behaviour of defer is useful... when
you don't wish to be limited by the current scope.

like this:

func f() {
if debugging {
fmt.Printf("inside f\n")
defer fmt.Printf("leaving f\n")
}
bodyOfFunction()
}

or this:

func f(out io.Writer) {
if out == nil {
out, _ = os.Open("/tmp/log", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
defer out.Close()
}
fmt.Fprintf(out, "output of f\n")
}

if you want a scope-defined defer, you can always break out the
code into a separate function, or even use an inline function:

for {
func(){


file, err := os.Open("/dev/null", os.O_RDONLY, 0)
defer file.Close()
// ...

}()

Ian Lance Taylor

unread,
Oct 13, 2010, 10:31:10 AM10/13/10
to Albert Strasheim, golang-nuts
Albert Strasheim <ful...@gmail.com> writes:

> What's the thinking behind only doing defer on function returns, as
> opposed to also doing it on for-loop "returns"?

There are times when it is useful to call defer in a loop. It's
straightforward to make your loop call a function if you want a defer to
run at each loop iteration. The reverse--providing a way to call defer
in a loop if all defer functions run each loop iteration--would be
difficult.

For example, here is a use of defer in a loop to implement a postorder
traversal of a binary tree without using explicit recursion (in effect
the recursion is exported to the defer stack).

func Postorder(t Tree, f func(Tree)) {
for t != nil {
defer f(t)
defer PostOrder(t.Left, f)
t = t.Right
}
}

Ian

chris dollin

unread,
Oct 13, 2010, 10:45:55 AM10/13/10
to fango, golang-nuts
On 13 October 2010 15:21, fango <fan.h...@gmail.com> wrote:
> But that's the status quo. Defer is a gift, not a over-engineered
> feature.

Defer is a feature of the language. It could be implemented in
various different ways. That it has a neat implementation in the
*g family of compilers is nice, but it's not the /reason/ that defer
is in the language.

(As far as I know: refutations welcome ...)

> I won't ask for more than the necessities.

We all ask for more than "the necessities", because the
necessities are so bare. (Subtraction? Functions? Long names?
Nested arithmetic expressions? If statements, given that we have
switch?)

> Anyway, any block can be put into a function call and use defer,
> doesn't it?

That's a reason why having for-scoped defers isn't /necessary/,
but it's not necessarily a reason for them being /omitted/. After
all, having to wrap block B into (func() {B})() isn't exactly
transparent.

Cory Mainwaring

unread,
Oct 13, 2010, 11:32:30 AM10/13/10
to chris dollin, fango, golang-nuts
Defers are useless at a block-level scope. If you have 12 files and
you iterate through them, spooling up files to close at the end of the
for loop, you have over-engineered your problem. If you only need to
access one file at a time, then close it after you're done doing
whatever you need:

for {
file, err := os.Open("/dev/null", os.O_RDONLY, 0)

// ...
file.Close()
}

If you need old files, then you need a counter and a way to access it
anyways, which means that you just close the oldest one.

for {
oldFile = file // Can be expanded to include any number of files


file, err = os.Open("/dev/null", os.O_RDONLY, 0)

// ...
oldFile.Close()
}

If you need these files until the function ends, use a defer. And, if
the for loop has tons of paths and you don't know where it's going to
pop out and a defer would be useful, drop it into a function and use
defer.

Reply all
Reply to author
Forward
0 new messages