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]].