github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/errors-are-values.article (about)

     1  Errors are values
     2  12 Jan 2015
     3  
     4  Rob Pike
     5  
     6  * Introduction
     7  
     8  A common point of discussion among Go programmers,
     9  especially those new to the language, is how to handle errors.
    10  The conversation often turns into a lament at the number of times the sequence
    11  
    12  	if err != nil {
    13  		return err
    14  	}
    15  
    16  shows up.
    17  We recently scanned all the open source projects we could find and
    18  discovered that this snippet occurs only once per page or two,
    19  less often than some would have you believe.
    20  Still, if the perception persists that one must type
    21  
    22  	if err != nil
    23  
    24  all the time, something must be wrong, and the obvious target is Go itself.
    25  
    26  This is unfortunate, misleading, and easily corrected.
    27  Perhaps what is happening is that programmers new to Go ask,
    28  "How does one handle errors?", learn this pattern, and stop there.
    29  In other languages, one might use a try-catch block or other such mechanism to handle errors.
    30  Therefore, the programmer thinks, when I would have used a try-catch
    31  in my old language, I will just type `if` `err` `!=` `nil` in Go.
    32  Over time the Go code collects many such snippets, and the result feels clumsy.
    33  
    34  Regardless of whether this explanation fits,
    35  it is clear that these Go programmers miss a fundamental point about errors:
    36  _Errors_are_values._
    37  
    38  Values can be programmed, and since errors are values, errors can be programmed.
    39  
    40  Of course a common statement involving an error value is to test whether it is nil,
    41  but there are countless other things one can do with an error value,
    42  and application of some of those other things can make your program better,
    43  eliminating much of the boilerplate that arises if every error is checked with a rote if statement.
    44  
    45  Here's a simple example from the `bufio` package's
    46  [[http://golang.org/pkg/bufio/#Scanner][`Scanner`]] type.
    47  Its [[http://golang.org/pkg/bufio/#Scanner.Scan][`Scan`]] method performs the underlying I/O,
    48  which can of course lead to an error.
    49  Yet the `Scan` method does not expose an error at all.
    50  Instead, it returns a boolean, and a separate method, to be run at the end of the scan,
    51  reports whether an error occurred.
    52  Client code looks like this:
    53  
    54  	scanner := bufio.NewScanner(input)
    55  	for scanner.Scan() {
    56  		token := scanner.Text()
    57  		// process token
    58  	}
    59  	if err := scanner.Err(); err != nil {
    60  		// process the error
    61  	}
    62  
    63  Sure, there is a nil check for an error, but it appears and executes only once.
    64  The `Scan` method could instead have been defined as
    65  
    66  	func (s *Scanner) Scan() (token []byte, error)
    67  
    68  and then the example user code might be (depending on how the token is retrieved),
    69  
    70  	scanner := bufio.NewScanner(input)
    71  	for {
    72  		token, err := scanner.Scan()
    73  		if err != nil {
    74  			return err // or maybe break
    75  		}
    76  		// process token
    77  	}
    78  
    79  This isn't very different, but there is one important distinction.
    80  In this code, the client must check for an error on every iteration,
    81  but in the real `Scanner` API, the error handling is abstracted away from the key API element,
    82  which is iterating over tokens.
    83  With the real API, the client's code therefore feels more natural:
    84  loop until done, then worry about errors.
    85  Error handling does not obscure the flow of control.
    86  
    87  Under the covers what's happening, of course,
    88  is that as soon as `Scan` encounters an I/O error, it records it and returns `false`.
    89  A separate method, [[http://golang.org/pkg/bufio/#Scanner.Err][`Err`]],
    90  reports the error value when the client asks.
    91  Trivial though this is, it's not the same as putting
    92  
    93  	if err != nil
    94  
    95  everywhere or asking the client to check for an error after every token.
    96  It's programming with error values.
    97  Simple programming, yes, but programming nonetheless.
    98  
    99  It's worth stressing that whatever the design,
   100  it's critical that the program check the errors however they are exposed.
   101  The discussion here is not about how to avoid checking errors,
   102  it's about using the language to handle errors with grace.
   103  
   104  The topic of repetitive error-checking code arose when I attended the autumn 2014 GoCon in Tokyo.
   105  An enthusiastic gopher, who goes by [[https://twitter.com/jxck_][`@jxck_`]] on Twitter,
   106  echoed the familiar lament about error checking.
   107  He had some code that looked schematically like this:
   108  
   109  	_, err = fd.Write(p0[a:b])
   110  	if err != nil {
   111  		return err
   112  	}
   113  	_, err = fd.Write(p1[c:d])
   114  	if err != nil {
   115  		return err
   116  	}
   117  	_, err = fd.Write(p2[e:f])
   118  	if err != nil {
   119  		return err
   120  	}
   121  	// and so on
   122  
   123  It is very repetitive.
   124  In the real code, which was longer,
   125  there is more going on so it's not easy to just refactor this using a helper function,
   126  but in this idealized form, a function literal closing over the error variable would help:
   127  
   128  	var err error
   129  	write := func(buf []byte) {
   130  		if err != nil {
   131  			return
   132  		}
   133  		_, err = w.Write(buf)
   134  	}
   135  	write(p0[a:b])
   136  	write(p1[c:d])
   137  	write(p2[e:f])
   138  	// and so on
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  This pattern works well, but requires a closure in each function doing the writes;
   144  a separate helper function is clumsier to use because the `err` variable
   145  needs to be maintained across calls (try it).
   146  
   147  We can make this cleaner, more general, and reusable by borrowing the idea from the
   148  `Scan` method above.
   149  I mentioned this technique in our discussion but `@jxck_` didn't see how to apply it.
   150  After a long exchange, hampered somewhat by a language barrier,
   151  I asked if I could just borrow his laptop and show him by typing some code.
   152  
   153  I defined an object called an `errWriter`, something like this:
   154  
   155  	type errWriter struct {
   156  		w   io.Writer
   157  		err error
   158  	}
   159  
   160  and gave it one method, `write.`
   161  It doesn't need to have the standard `Write` signature,
   162  and it's lower-cased in part to highlight the distinction.
   163  The `write` method calls the `Write` method of the underlying `Writer`
   164  and records the first error for future reference:
   165  
   166  	func (ew *errWriter) write(buf []byte) {
   167  		if ew.err != nil {
   168  			return
   169  		}
   170  		_, ew.err = ew.w.Write(buf)
   171  	}
   172  
   173  As soon as an error occurs, the `write` method becomes a no-op but the error value is saved.
   174  
   175  Given the `errWriter` type and its `write` method, the code above can be refactored:
   176  
   177  	ew := &errWriter{w: fd}
   178  	ew.write(p0[a:b])
   179  	ew.write(p1[c:d])
   180  	ew.write(p2[e:f])
   181  	// and so on
   182  	if ew.err != nil {
   183  		return ew.err
   184  	}
   185  
   186  This is cleaner, even compared to the use of a closure,
   187  and also makes the actual sequence of writes being done easier to see on the page.
   188  There is no clutter any more.
   189  Programming with error values (and interfaces) has made the code nicer.
   190  
   191  It's likely that some other piece of code in the same package can build on this idea,
   192  or even use `errWriter` directly.
   193  
   194  Also, once `errWriter` exists, there's more it could do to help,
   195  especially in less artificial examples.
   196  It could accumulate the byte count.
   197  It could coalesce writes into a single buffer that can then be transmitted atomically.
   198  And much more.
   199  
   200  In fact, this pattern appears often in the standard library.
   201  The [[http://golang.org/pkg/archive/zip/][`archive/zip`]] and
   202  [[http://golang.org/pkg/net/http/][`net/http`]] packages use it.
   203  More salient to this discussion, the [[http://golang.org/pkg/bufio/][`bufio` package's `Writer`]]
   204  is actually an implementation of the `errWriter` idea.
   205  Although `bufio.Writer.Write` returns an error,
   206  that is mostly about honoring the [[http://golang.org/pkg/io/#Writer][`io.Writer`]] interface.
   207  The `Write` method of `bufio.Writer` behaves just like our `errWriter.write`
   208  method above, with `Flush` reporting the error, so our example could be written like this:
   209  
   210  	b := bufio.NewWriter(fd)
   211  	b.Write(p0[a:b])
   212  	b.Write(p1[c:d])
   213  	b.Write(p2[e:f])
   214  	// and so on
   215  	if b.Flush() != nil {
   216  		return b.Flush()
   217  	}
   218  
   219  There is one significant drawback to this approach, at least for some applications:
   220  there is no way to know how much of the processing completed before the error occurred.
   221  If that information is important, a more fine-grained approach is necessary.
   222  Often, though, an all-or-nothing check at the end is sufficient.
   223  
   224  We've looked at just one technique for avoiding repetitive error handling code.
   225  Keep in mind that the use of `errWriter` or `bufio.Writer` isn't the only way to simplify error handling,
   226  and this approach is not suitable for all situations.
   227  The key lesson, however, is that errors are values and the full power of
   228  the Go programming language is available for processing them.
   229  
   230  Use the language to simplify your error handling.
   231  
   232  But remember: Whatever you do, always check your errors!
   233  
   234  Finally, for the full story of my interaction with @jxck_, including a little video he recorded,
   235  visit [[http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike][his blog]].