diff options
Diffstat (limited to 'doc/articles/error_handling.html')
-rw-r--r-- | doc/articles/error_handling.html | 316 |
1 files changed, 0 insertions, 316 deletions
diff --git a/doc/articles/error_handling.html b/doc/articles/error_handling.html deleted file mode 100644 index 6ba05ac1d..000000000 --- a/doc/articles/error_handling.html +++ /dev/null @@ -1,316 +0,0 @@ -<!--{ - "Title": "Error Handling and Go", - "Template": true -}--> - -<p> -If you have written any Go code you have probably encountered the built-in -<code>error</code> type. Go code uses <code>error</code> values to -indicate an abnormal state. For example, the <code>os.Open</code> function -returns a non-nil <code>error</code> value when it fails to open a file. -</p> - -{{code "/doc/progs/error.go" `/func Open/`}} - -<p> -The following code uses <code>os.Open</code> to open a file. If an error -occurs it calls <code>log.Fatal</code> to print the error message and stop. -</p> - -{{code "/doc/progs/error.go" `/func openFile/` `/STOP/`}} - -<p> -You can get a lot done in Go knowing just this about the <code>error</code> -type, but in this article we'll take a closer look at <code>error</code> and -discuss some good practices for error handling in Go. -</p> - -<p> -<b>The error type</b> -</p> - -<p> -The <code>error</code> type is an interface type. An <code>error</code> -variable represents any value that can describe itself as a string. Here is the -interface's declaration: -</p> - -<pre>type error interface { - Error() string -}</pre> - -<p> -The <code>error</code> type, as with all built in types, is -<a href="/doc/go_spec.html#Predeclared_identifiers">predeclared</a> in the -<a href="/doc/go_spec.html#Blocks">universe block</a>. -</p> - -<p> -The most commonly-used <code>error</code> implementation is the -<a href="/pkg/errors/">errors</a> package's unexported <code>errorString</code> type. -</p> - -{{code "/doc/progs/error.go" `/errorString/` `/STOP/`}} - -<p> -You can construct one of these values with the <code>errors.New</code> -function. It takes a string that it converts to an <code>errors.errorString</code> -and returns as an <code>error</code> value. -</p> - -{{code "/doc/progs/error.go" `/New/` `/STOP/`}} - -<p> -Here's how you might use <code>errors.New</code>: -</p> - -{{code "/doc/progs/error.go" `/func Sqrt/` `/STOP/`}} - -<p> -A caller passing a negative argument to <code>Sqrt</code> receives a non-nil -<code>error</code> value (whose concrete representation is an -<code>errors.errorString</code> value). The caller can access the error string -("math: square root of...") by calling the <code>error</code>'s -<code>Error</code> method, or by just printing it: -</p> - -{{code "/doc/progs/error.go" `/func printErr/` `/STOP/`}} - -<p> -The <a href="/pkg/fmt/">fmt</a> package formats an <code>error</code> value -by calling its <code>Error() string</code> method. -</p> - -<p> -It is the error implementation's responsibility to summarize the context. -The error returned by <code>os.Open</code> formats as "open /etc/passwd: -permission denied," not just "permission denied." The error returned by our -<code>Sqrt</code> is missing information about the invalid argument. -</p> - -<p> -To add that information, a useful function is the <code>fmt</code> package's -<code>Errorf</code>. It formats a string according to <code>Printf</code>'s -rules and returns it as an <code>error</code> created by -<code>errors.New</code>. -</p> - -{{code "/doc/progs/error.go" `/fmtError/` `/STOP/`}} - -<p> -In many cases <code>fmt.Errorf</code> is good enough, but since -<code>error</code> is an interface, you can use arbitrary data structures as -error values, to allow callers to inspect the details of the error. -</p> - -<p> -For instance, our hypothetical callers might want to recover the invalid -argument passed to <code>Sqrt</code>. We can enable that by defining a new -error implementation instead of using <code>errors.errorString</code>: -</p> - -{{code "/doc/progs/error.go" `/type NegativeSqrtError/` `/STOP/`}} - -<p> -A sophisticated caller can then use a -<a href="/doc/go_spec.html#Type_assertions">type assertion</a> to check for a -<code>NegativeSqrtError</code> and handle it specially, while callers that just -pass the error to <code>fmt.Println</code> or <code>log.Fatal</code> will see -no change in behavior. -</p> - -<p> -As another example, the <a href="/pkg/encoding/json/">json</a> package specifies a -<code>SyntaxError</code> type that the <code>json.Decode</code> function -returns when it encounters a syntax error parsing a JSON blob. -</p> - -{{code "/doc/progs/error.go" `/type SyntaxError/` `/STOP/`}} - -<p> -The <code>Offset</code> field isn't even shown in the default formatting of the -error, but callers can use it to add file and line information to their error -messages: -</p> - -{{code "/doc/progs/error.go" `/func decodeError/` `/STOP/`}} - -<p> -(This is a slightly simplified version of some -<a href="http://golang.org/s/camjsondecode">actual code</a> -from the <a href="http://camlistore.org">Camlistore</a> project.) -</p> - -<p> -The <code>error</code> interface requires only a <code>Error</code> method; -specific error implementations might have additional methods. For instance, the -<a href="/pkg/net/">net</a> package returns errors of type -<code>error</code>, following the usual convention, but some of the error -implementations have additional methods defined by the <code>net.Error</code> -interface: -</p> - -<pre>package net - -type Error interface { - error - Timeout() bool // Is the error a timeout? - Temporary() bool // Is the error temporary? -}</pre> - -<p> -Client code can test for a <code>net.Error</code> with a type assertion and -then distinguish transient network errors from permanent ones. For instance, a -web crawler might sleep and retry when it encounters a temporary error and give -up otherwise. -</p> - -{{code "/doc/progs/error.go" `/func netError/` `/STOP/`}} - -<p> -<b>Simplifying repetitive error handling</b> -</p> - -<p> -In Go, error handling is important. The language's design and conventions -encourage you to explicitly check for errors where they occur (as distinct from -the convention in other languages of throwing exceptions and sometimes catching -them). In some cases this makes Go code verbose, but fortunately there are some -techniques you can use to minimize repetitive error handling. -</p> - -<p> -Consider an <a href="http://code.google.com/appengine/docs/go/">App Engine</a> -application with an HTTP handler that retrieves a record from the datastore and -formats it with a template. -</p> - -{{code "/doc/progs/error2.go" `/func init/` `/STOP/`}} - -<p> -This function handles errors returned by the <code>datastore.Get</code> -function and <code>viewTemplate</code>'s <code>Execute</code> method. In both -cases, it presents a simple error message to the user with the HTTP status code -500 ("Internal Server Error"). This looks like a manageable amount of code, but -add some more HTTP handlers and you quickly end up with many copies of -identical error handling code. -</p> - -<p> -To reduce the repetition we can define our own HTTP <code>appHandler</code> -type that includes an <code>error</code> return value: -</p> - -{{code "/doc/progs/error3.go" `/type appHandler/`}} - -<p> -Then we can change our <code>viewRecord</code> function to return errors: -</p> - -{{code "/doc/progs/error3.go" `/func viewRecord/` `/STOP/`}} - -<p> -This is simpler than the original version, but the <a -href="/pkg/net/http/">http</a> package doesn't understand functions that return -<code>error</code>. -To fix this we can implement the <code>http.Handler</code> interface's -<code>ServeHTTP</code> method on <code>appHandler</code>: -</p> - -{{code "/doc/progs/error3.go" `/ServeHTTP/` `/STOP/`}} - -<p> -The <code>ServeHTTP</code> method calls the <code>appHandler</code> function -and displays the returned error (if any) to the user. Notice that the method's -receiver, <code>fn</code>, is a function. (Go can do that!) The method invokes -the function by calling the receiver in the expression <code>fn(w, r)</code>. -</p> - -<p> -Now when registering <code>viewRecord</code> with the http package we use the -<code>Handle</code> function (instead of <code>HandleFunc</code>) as -<code>appHandler</code> is an <code>http.Handler</code> (not an -<code>http.HandlerFunc</code>). -</p> - -{{code "/doc/progs/error3.go" `/func init/` `/STOP/`}} - -<p> -With this basic error handling infrastructure in place, we can make it more -user friendly. Rather than just displaying the error string, it would be better -to give the user a simple error message with an appropriate HTTP status code, -while logging the full error to the App Engine developer console for debugging -purposes. -</p> - -<p> -To do this we create an <code>appError</code> struct containing an -<code>error</code> and some other fields: -</p> - -{{code "/doc/progs/error4.go" `/type appError/` `/STOP/`}} - -<p> -Next we modify the appHandler type to return <code>*appError</code> values: -</p> - -{{code "/doc/progs/error4.go" `/type appHandler/`}} - -<p> -(It's usually a mistake to pass back the concrete type of an error rather than -<code>error</code>, -for reasons discussed in <a href="/doc/go_faq.html#nil_error">the Go FAQ</a>, -but it's the right thing to do here because <code>ServeHTTP</code> is the only -place that sees the value and uses its contents.) -</p> - -<p> -And make <code>appHandler</code>'s <code>ServeHTTP</code> method display the -<code>appError</code>'s <code>Message</code> to the user with the correct HTTP -status <code>Code</code> and log the full <code>Error</code> to the developer -console: -</p> - -{{code "/doc/progs/error4.go" `/ServeHTTP/` `/STOP/`}} - -<p> -Finally, we update <code>viewRecord</code> to the new function signature and -have it return more context when it encounters an error: -</p> - -{{code "/doc/progs/error4.go" `/func viewRecord/` `/STOP/`}} - -<p> -This version of <code>viewRecord</code> is the same length as the original, but -now each of those lines has specific meaning and we are providing a friendlier -user experience. -</p> - -<p> -It doesn't end there; we can further improve the error handling in our -application. Some ideas: -</p> - -<ul> -<li>give the error handler a pretty HTML template, -<li>make debugging easier by writing the stack trace to the HTTP response when -the user is an administrator, -<li>write a constructor function for <code>appError</code> that stores the -stack trace for easier debugging, -<li>recover from panics inside the <code>appHandler</code>, logging the error -to the console as "Critical," while telling the user "a serious error -has occurred." This is a nice touch to avoid exposing the user to inscrutable -error messages caused by programming errors. -See the <a href="defer_panic_recover.html">Defer, Panic, and Recover</a> -article for more details. -</ul> - -<p> -<b>Conclusion</b> -</p> - -<p> -Proper error handling is an essential requirement of good software. By -employing the techniques described in this post you should be able to write -more reliable and succinct Go code. -</p> |