Making error great again
The following is a canonical example of how horrible error handling can become in Go:
_, err = fd.Write(p0[a:b])
if err != nil {
return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
return err
}
// and so on
An improvement, using a helper function:
var err error
write := func(buf []byte) {
if err != nil {
return
}
_, err = w.Write(buf)
}
write(p0[a:b])
write(p1[c:d])
write(p2[e:f])
// and so on
if err != nil {
return err
}
However, here we need to be sure that err
is being captured by write
every time we call this function. We could actually do better, by creating a
new type, and adding a method for it that does what write
is doing here. Then,
we will not need to have any closure, nor ensure that the value is being closed
by the function.
This is even a much better way of working with errors. And in some ways, it brings to the table some of Haskell's/FP approach to error handling:
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) write(buf []byte) {
if ew.err != nil {
return
}
_, ew.err = ew.w.Write(buf)
}
ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
return ew.err
}
Here, write
checks whether errWriter
have failed before. If it
did, then it won't do anything again; we are an abnormal state already.
Otherwise, it's free to do some work.
References:
- https://blog.golang.org/errors-are-values
- http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike