What I like about error handling in Go is that errors are just values. Like 
any other values, they can be transparently stored, aggregated, passed 
around and used, among other things, to explicitly change program flow. 
That is why I am on the whole satisfied by Go 1's approach and a bit 
concerned about many of the Go 2 proposals that provide specialized syntax 
for handling errors.

I know that checking for errors can lead to repetitive and verbose code. My 
solution has been to refactor repetitive error handling logic into a 
package-level function or a method on a receiver (or even a closure within 
a method or function) depending on needs. For example in a parser package, 
I might have a single method of the parser object that aggregates all 
parsing errors and then provide an Err() method that can be used by caller 
to check for and report any errors. This is the same approach used in 
several occasions in the standard library and often results in clean and 
idiomatic interfaces.

This approach does not work as well in some scenarios, e.g., in routines 
reading and writing files, where there are different types of errors 
potentially returned at every step of opening, reading, creating, writing 
and closing files and often requiring bailing out from the routine 
hopefully with an informative error message. This often results in a code 
like this:

func CopyFile(src, dst string) error {
 r, err := os.Open(src)
 if err != nil {
 return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }
 defer r.Close()


 w, err := os.Create(dst)
 if err != nil {
 return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }


 if _, err := io.Copy(w, r); err != nil {
 w.Close()
 os.Remove(dst)
 return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }


 if err := w.Close(); err != nil {
 os.Remove(dst)
 return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }
  return nil
}


source: Russ Cox. Error Handling — Problem Overview 
https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md

My approach currently is to try and reduce the clutter and repetitiveness 
by creating one or more closures having the same return signature as the 
enclosing function, so that I can return it if error is not null. That 
results in the following code which for me represents a reasonable trade 
off between avoiding repetitiveness and readability. 



func CopyFile(dst, src string) error {
 report:= func(err error) error {
    return fmt.Errorf("failed to copy %s to %s: %v", src, dst, err)
 }
 closeAndReport:= func(w io.WriteCloser, err error) error {
    w.Close()    //works because closing an already closed file is ok
    os.Remove(dst) 
    return report(err)
 }
 r, err := os.Open(src)
 if err != nil {
 return report(err)
 }
 defer r.Close()


 w, err := os.Create(dst)
 if err != nil {
 return report(err)
 }


 if _, err := io.Copy(w, r); err != nil {
 return closeAndReport(w, err)
 }


 if err := w.Close(); err != nil {
 return closeAndReport(w, err)
 }
 return nil
}

Try it here https://play.golang.org/p/5INz2oW5c8p

For me, the only needed change here is perhaps a mechanism to simplify the 
if err!= nil { return myClosure()} bit. I guess this is what the proposed 
"check" keyword in Russ's proposal is supposed to do. For example, 

r, err := os.Open(src)
if err != nil {
  return report(err)
}



becomes

r := check os.Open(src)


As many have pointed out, the check keyword has several shortcomings the 
most important for me are that:
(1) it obscures the change in workflow and (2) it treats error values 
differently than all other values.

Overall, I do not think that the gain in brevity compensates for the loss 
in code clarity (and for the addition of 2 new keywords). Perhaps I am 
lacking in imagination, but I am finding it really hard to think of ways to 
beat the exquisite balancing act that Go 1 achieves when it comes to 
handling errors and exceptions. 

What do  you think?

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to