Tiny func to close io.Closer safely
When you finish file processing or reading HTTP response, you should call Close()
func and do exit processing. You may use a simple call with defer
.
func hoge() error {
f, err := os.Open("/path/to/file")
if err != nil {
return err
}
defer f.Close()
...
}
But this has some problems.
The type of value is error
. That is, the process may fail, and you sometime want to log the error message.
kisielk/errcheck is a bit strict linter. It forbids to ignore return values like this. You can avoid this by using _
and illustrating the existence of return values,
defer func() { _ = f.Close() }()
or it is also good not to use defer
.
func hoge() error {
f, err := os.Open("/path/to/file")
if err != nil {
return err
}
...
return f.Close()
}
People issue this many times (#55, #77, #101), but they are all rejected. The author may think error
's should be checked explicitly.
This package solves them all.
func hoge() (err error) {
f, err := os.Open("/path/to/file")
if err != nil {
return err
}
defer closer.Close(f, &err)
...
}
That's it! This code manages the exit processing and can report the error when it occurs.
The point is the signature: (err error)
. The defer
block can overwrite the value err
-- the named return value. You may not have used named return values. You need (almost) not to change how to write functions.
func fuga() (_ []int, err error) {
f, err := os.Open("/path/to/file")
if err != nil {
return nil, err
}
defer closer.Close(f, &err)
...
return result, nil
}
There are no problems when the function have multiple return values. You can use _
as the values that do not need names.
You sometime want to clean up after opening tempfiles.
func hogefuga() error {
f, err := ioutil.TempFile("", "hogefuga")
if err != nil {
return err
}
defer os.Remove(f.Name()) // This ignores the error!
...
return nil
}
You cannot use close.Close()
because os.Remove()
is not io.Closer
. You can use more general function: closer.Check()
.
func fugahoge() (err error) {
f, err := ioutil.TempFile("", "fugahoge")
if err != nil {
return err
}
defer closer.Check(func() error { return os.Remove(f.Name()) }, &err)
...
return nil
}
- Don’t defer Close() on writable files – joe shaw
- An entry to explain a matter when you do not read the return value of
Close()
.
- An entry to explain a matter when you do not read the return value of
github.com/src-d/go-git/utils/ioutil.CheckClose()
- This idea of here is the implementation of this repo. But it has no tests and the repo is too huge for the use of this use. So I created this package.