How to open, write, and close file properly?

9,409 views
Skip to first unread message

Lambda

unread,
Apr 12, 2010, 10:26:10 AM4/12/10
to golang-nuts
I'd like to open a file, write several lines of strings, and finally
close the file.
As there are several places that we can meet errors, we have to make
sure the file is closed,
despite possible errors.
Go has the defer statement.
My understanding is that its purpose is guarantee the close operation,
in this file case.
Is my understanding right? If I properly handle every possible error,
need I use defer any more?
Can I call close() and the end, without the defer statement?

And can you provide an example. In my case, I need 3 operations:

func Write(fname, content string) {
open()

write()

close()
}
Where should I put the defer statement?

chris dollin

unread,
Apr 12, 2010, 10:36:24 AM4/12/10
to Lambda, golang-nuts
On 12 April 2010 15:26, Lambda <steph...@gmail.com> wrote:
I'd like to open a file, write several lines of strings, and finally
close the file.
As there are several places that we can meet errors, we have to make
sure the file is closed,
despite possible errors.
Go has the defer statement.
My understanding is that its purpose is guarantee the close operation,
in this file case.
Is my understanding right? If I properly handle every possible error,
need I use defer any more?

defer is part of /how/ you properly handle " every possible error".

Can I call close() and the end, without the defer statement?

Yes. Then you risk not closing the file if a panic happens.
 
And can you provide an example. In my case, I need 3 operations:

func Write(fname, content string) {
open()

write()

close()
}
Where should I put the defer statement?

After the open (otherwise nothing to close), but before you do
any writing (otherwise might miss an error).

--
Chris "allusive" Dollin

Lambda

unread,
Apr 12, 2010, 10:51:04 AM4/12/10
to golang-nuts
> Yes. Then you risk not closing the file if a panic happens.

Not like normal error, panic is out of my control, right?
Where can I find the document of panic?

chris dollin

unread,
Apr 12, 2010, 10:58:28 AM4/12/10
to Lambda, golang-nuts
On 12 April 2010 15:51, Lambda <steph...@gmail.com> wrote:
> Yes. Then you risk not closing the file if a panic happens.

Not like normal error, panic is out of my control, right?

The panics aren't under your control, but the option to recover
is.
 
Where can I find the document of panic?

The language spec describes the current version of panic & recover.

Note that the panicing goroutine will execute pending defers, so
using a defer to .Close a file means it will close even if there's
a panic in this goroutine.

(I assume the Go team are waiting for panic etc to settle down before
putting stuff into Effective Go and perhaps the tutorial.)

Chris

--
Chris "allusive" Dollin

Andrew Gerrand

unread,
Apr 12, 2010, 7:17:31 PM4/12/10
to Lambda, golang-nuts
A simple example:

func doWrite() (err os.Error) {
f, err := os.Open("somefile", os.O_RDWR | os.O_CREATE, 0666)
if err != nil {
return
}

defer f.Close()

_, err = f.Write([]byte("An array of bytes"))
if err != nil {
return // f.Close() will automatically be called now
}

// some other logic here

return // f.Close() will automatically be called now
}

You could, just as easily, call f.close() before any return statement
in the function. That would be fine.

Don't confuse defer with panic/resolve. The panic/resolve mechanism
uses defer, but defer is a useful feature on its own.

You shouldn't put defer statements in your code to guard against
unexpected panics. No well-written library should let a panic escape
the package itself.

In this specific case, defer is simply a nice way of avoiding typing
f.Close() more than once.

Andrew

> --
> To unsubscribe, reply using "remove me" as the subject.
>

andrey mirtchovski

unread,
Apr 12, 2010, 7:34:56 PM4/12/10
to golang-nuts
(forgot the reply-all, sorry)

> In this specific case, defer is simply a nice way of avoiding typing
> f.Close() more than once.

more importantly, defer lets the programmer keep related actions in
proximity to each other in the code -- seeing the 'defer close()'
statement right after the Open (i wouldn't even separate it with an
empty line) tells me much more about the action than i would otherwise
know: "whatever else happens, this needs to close when i'm done". i
know locks are unpopular in Go, but i'm positive the defer statement
came about from a desire to solve the problem of unlocking held locks
in multiple code paths, a source of deadlocks in a lot of software,
even one involving channels and coroutines. defer is a very elegant
solution (and a crutch for sloppy programmers) and it does keep
services running.

Lambda

unread,
Apr 13, 2010, 2:30:59 AM4/13/10
to golang-nuts
What if I call os.exit(1) instead of return, will the defer work?

On 4月13日, 上午7時17分, Andrew Gerrand <a...@golang.org> wrote:
> A simple example:
>
> func doWrite() (err os.Error) {
>     f, err := os.Open("somefile", os.O_RDWR | os.O_CREATE, 0666)
>     if err != nil {
>         return
>     }
>
>     defer f.Close()
>
>     _, err = f.Write([]byte("An array of bytes"))
>     if err != nil {
>         return // f.Close() will automatically be called now
>     }
>
>     // some other logic here
>
>     return // f.Close() will automatically be called now
>
> }
>
> You could, just as easily, call f.close() before any return statement
> in the function. That would be fine.
>
> Don't confuse defer with panic/resolve. The panic/resolve mechanism
> uses defer, but defer is a useful feature on its own.
>
> You shouldn't put defer statements in your code to guard against
> unexpected panics. No well-written library should let a panic escape
> the package itself.
>
> In this specific case, defer is simply a nice way of avoiding typing
> f.Close() more than once.
>
> Andrew
>

Benny Siegert

unread,
Apr 13, 2010, 8:00:41 AM4/13/10
to golan...@googlegroups.com
On Tue, Apr 13, 2010 at 08:30, Lambda <steph...@gmail.com> wrote:
> What if I call os.exit(1) instead of return, will the defer work?

Then it does not matter, as the operating system will close and flush
any open file descriptors on exit.

--Benny.

David Roundy

unread,
Apr 14, 2010, 11:18:02 AM4/14/10
to Benny Siegert, golan...@googlegroups.com
Unless you decide to use bufio, since the operating system doesn't
know about bufio. So if you use bufio, you probably don't want to use
os.Exit, unless you also manually flush after every write that you
don't want to lose (which isn't the point of bufio...).

David

yy

unread,
Apr 14, 2010, 12:13:04 PM4/14/10
to David Roundy, Benny Siegert, golan...@googlegroups.com
2010/4/14 David Roundy <rou...@physics.oregonstate.edu>:

> Unless you decide to use bufio, since the operating system doesn't
> know about bufio.  So if you use bufio, you probably don't want to use
> os.Exit, unless you also manually flush after every write that you
> don't want to lose (which isn't the point of bufio...).
>

Or you can defer the call to os.Exit, with something like:

package main

import ("bufio"; "os")

type ExitCode int

func End(){
if x := recover(); x != nil {
if c, ok := x.(ExitCode); ok {
os.Exit(int(c))
}
panic(x)
}
}

func Exit(code int){
panic(ExitCode(code))
}

func main(){
defer End()
b := bufio.NewWriter(os.Stdout)
defer b.Flush()
b.Write([]byte("Halo!\n"))
Exit(666)
}

Maybe os.Exit() should call the defered functions?

--
- yiyus || JGL . 4l77.com

David Roundy

unread,
Apr 14, 2010, 1:20:33 PM4/14/10
to yy, Benny Siegert, golan...@googlegroups.com
On Wed, Apr 14, 2010 at 9:13 AM, yy <yiyu...@gmail.com> wrote:
> Or you can defer the call to os.Exit, with something like:

Yeah, that's how I'd prefer for os.Exit to work, although there is
some vagueness in my mind as to how panics are handled in goroutines.
Obviously a panic in a goroutine will kill the entire program, but
does it trigger all the deferred functions in all other goroutines? I
would hope so, but if not, then your Exit may not work properly.

David

Micah Stetson

unread,
Apr 30, 2010, 10:21:36 PM4/30/10
to golang-nuts
> func doWrite() (err os.Error) {
>     f, err := os.Open("somefile", os.O_RDWR | os.O_CREATE, 0666)
>     if err != nil {
>         return
>     }
>     defer f.Close()

I know this is an old thread, but I've been thinking about this:

http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html#NOTES

Using defer as above ignores any error from f.Close(), which the Linux
man page claims to be a "serious programming error". Would something
like this (untested) be better?

defer func() {
e := f.Close()
if err == nil {
err = e
}
}

That way the error (if any) from Close isn't ignored, but doesn't
override another error being returned.

Micah

Rob 'Commander' Pike

unread,
Apr 30, 2010, 11:12:53 PM4/30/10
to Micah Stetson, golang-nuts

On Apr 30, 2010, at 7:21 PM, Micah Stetson wrote:

>> func doWrite() (err os.Error) {
>> f, err := os.Open("somefile", os.O_RDWR | os.O_CREATE, 0666)
>> if err != nil {
>> return
>> }
>> defer f.Close()
>
> I know this is an old thread, but I've been thinking about this:
>
> http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html#NOTES
>
> Using defer as above ignores any error from f.Close(), which the Linux
> man page claims to be a "serious programming error".

Why? How would my program behave differently if a close failed?

-rob

Micah Stetson

unread,
May 1, 2010, 12:06:46 AM5/1/10
to golang-nuts
> > Using defer as above ignores any error from f.Close(), which the Linux
> > man page claims to be a "serious programming error".
>
> Why?  How would my program behave differently if a close failed?

Part of why I'm asking is to have somebody credible tell me the man
page is just alarmist. If I take it seriously, though, it sounds like
an error on close really means a previous write failed. So your
program would do whatever it does on a write failure.

I'd like to say that it's the OS's problem, rather than my program's,
if a "successful" write later fails. But the Linux man page has a
scary note, and the OS X man page mentions it as a possibility, so I'm
wondering if I should be more paranoid.

If there's no reason to check it, Close should be changed not to
return an os.Error, right?

Micah

Rob 'Commander' Pike

unread,
May 1, 2010, 1:04:08 AM5/1/10
to Micah Stetson, golang-nuts
In Plan 9, close cannot error for just this reason. Unfortunately, that is irrelevant here because in Unix, it can. Thus the interface must provide the information back to the programmer. We can't control that.

If the situation requires you to handle errors on close, handle them. If it does not, you can defer the close. That's all there is to it.

-rob


Giles Lean

unread,
May 1, 2010, 1:37:55 AM5/1/10
to Micah Stetson, golang-nuts

Micah Stetson <micah....@gmail.com> wrote:

> Part of why I'm asking is to have somebody credible tell me the man
> page is just alarmist.

Sadly, it's not. Or at least theoretically not.

> If I take it seriously, though, it sounds like an error on
> close really means a previous write failed.

Yes. The prototypical case is a delayed write error over NFS;
whether you care and what you can do about it depends upon
your application.

I submit that most people don't care: if they did, disks
wouldn't ship with write back caching turned on as at least
most disks marketed to PC users do.

A SMTP server might choose to delete the file it had the
close() error on and return a temporary failure.

Databases running over NFS (which personally I don't find a
good idea at the best of times) also want to know, I presume,
although as they can't tell _which_ earlier write() failed I
don't know what good it would do them.

Finally, logging the error may let you figure out if your code
is mistakenly closing some file twice and another not at all.

On a traditional local file system, close() pretty much can't
fail when given an open file descriptor without something
being seriously wrong -- kernel bug, hardware error, etc in
which case as Rob Pike points out there isn't much your
program can do differently to cope with the error.

But mix in a remote file system (or a user space file system,
I imagine) and the potential failure modes are considerably
expanded, and I would be wary of close() errors in any program
where I considered the data written to be important.

Regards,

Giles

Micah Stetson

unread,
May 1, 2010, 2:06:29 AM5/1/10
to Rob 'Commander' Pike, golang-nuts
> If the situation requires you to handle errors
> on close, handle them. If it does not, you can
> defer the close. That's all there is to it.

It sounds obvious when you say it like that (kicks himself for having
to ask). Thanks, both.

Micah
Reply all
Reply to author
Forward
0 new messages