Skip to content

delphinus/go-closer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-closer

Tiny func to close io.Closer safely

What's this?

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.

1. It ignores the return value from Close().

The type of value is error. That is, the process may fail, and you sometime want to log the error message.

2. errcheck claims them

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.

Usage

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.

How to write code for other than io.Closer.

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
}

See also

About

Tiny func to close io.Closer safely

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages