diff options
Diffstat (limited to 'doc/articles/error_handling.html')
-rw-r--r-- | doc/articles/error_handling.html | 167 |
1 files changed, 25 insertions, 142 deletions
diff --git a/doc/articles/error_handling.html b/doc/articles/error_handling.html index ac33f1dab..8f4fffb48 100644 --- a/doc/articles/error_handling.html +++ b/doc/articles/error_handling.html @@ -1,10 +1,7 @@ <!--{ - "Title": "Error Handling and Go" + "Title": "Error Handling and Go", + "Template": true }--> -<!-- - DO NOT EDIT: created by - tmpltohtml articles/error_handling.tmpl ---> <p> If you have written any Go code you have probably encountered the built-in @@ -13,20 +10,14 @@ 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> -<pre><!--{{code "progs/error.go" `/func Open/`}} --->func Open(name string) (file *File, err error)</pre> +{{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> -<pre><!--{{code "progs/error.go" `/func openFile/` `/STOP/`}} ---> f, err := os.Open("filename.ext") - if err != nil { - log.Fatal(err) - } - // do something with the open *File f</pre> +{{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> @@ -59,15 +50,7 @@ The most commonly-used <code>error</code> implementation is the <a href="/pkg/errors/">errors</a> package's unexported <code>errorString</code> type. </p> -<pre><!--{{code "progs/error.go" `/errorString/` `/STOP/`}} --->// errorString is a trivial implementation of error. -type errorString struct { - s string -} - -func (e *errorString) Error() string { - return e.s -}</pre> +{{code "/doc/progs/error.go" `/errorString/` `/STOP/`}} <p> You can construct one of these values with the <code>errors.New</code> @@ -75,23 +58,13 @@ function. It takes a string that it converts to an <code>errors.errorString</cod and returns as an <code>error</code> value. </p> -<pre><!--{{code "progs/error.go" `/New/` `/STOP/`}} --->// New returns an error that formats as the given text. -func New(text string) error { - return &errorString{text} -}</pre> +{{code "/doc/progs/error.go" `/New/` `/STOP/`}} <p> Here's how you might use <code>errors.New</code>: </p> -<pre><!--{{code "progs/error.go" `/func Sqrt/` `/STOP/`}} --->func Sqrt(f float64) (float64, error) { - if f < 0 { - return 0, errors.New("math: square root of negative number") - } - // implementation -}</pre> +{{code "/doc/progs/error.go" `/func Sqrt/` `/STOP/`}} <p> A caller passing a negative argument to <code>Sqrt</code> receives a non-nil @@ -101,11 +74,7 @@ A caller passing a negative argument to <code>Sqrt</code> receives a non-nil <code>Error</code> method, or by just printing it: </p> -<pre><!--{{code "progs/error.go" `/func printErr/` `/STOP/`}} ---> f, err := Sqrt(-1) - if err != nil { - fmt.Println(err) - }</pre> +{{code "/doc/progs/error.go" `/func printErr/` `/STOP/`}} <p> The <a href="/pkg/fmt/">fmt</a> package formats an <code>error</code> value @@ -126,10 +95,7 @@ rules and returns it as an <code>error</code> created by <code>errors.New</code>. </p> -<pre><!--{{code "progs/error.go" `/fmtError/` `/STOP/`}} ---> if f < 0 { - return 0, fmt.Errorf("math: square root of negative number %g", f) - }</pre> +{{code "/doc/progs/error.go" `/fmtError/` `/STOP/`}} <p> In many cases <code>fmt.Errorf</code> is good enough, but since @@ -143,12 +109,7 @@ argument passed to <code>Sqrt</code>. We can enable that by defining a new error implementation instead of using <code>errors.errorString</code>: </p> -<pre><!--{{code "progs/error.go" `/type NegativeSqrtError/` `/STOP/`}} --->type NegativeSqrtError float64 - -func (f NegativeSqrtError) Error() string { - return fmt.Sprintf("math: square root of negative number %g", float64(f)) -}</pre> +{{code "/doc/progs/error.go" `/type NegativeSqrtError/` `/STOP/`}} <p> A sophisticated caller can then use a @@ -164,13 +125,7 @@ As another example, the <a href="/pkg/encoding/json/">json</a> package specifies returns when it encounters a syntax error parsing a JSON blob. </p> -<pre><!--{{code "progs/error.go" `/type SyntaxError/` `/STOP/`}} --->type SyntaxError struct { - msg string // description of error - Offset int64 // error occurred after reading Offset bytes -} - -func (e *SyntaxError) Error() string { return e.msg }</pre> +{{code "/doc/progs/error.go" `/type SyntaxError/` `/STOP/`}} <p> The <code>Offset</code> field isn't even shown in the default formatting of the @@ -178,14 +133,7 @@ error, but callers can use it to add file and line information to their error messages: </p> -<pre><!--{{code "progs/error.go" `/func decodeError/` `/STOP/`}} ---> if err := dec.Decode(&val); err != nil { - if serr, ok := err.(*json.SyntaxError); ok { - line, col := findLine(f, serr.Offset) - return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err) - } - return err - }</pre> +{{code "/doc/progs/error.go" `/func decodeError/` `/STOP/`}} <p> (This is a slightly simplified version of some @@ -217,14 +165,7 @@ web crawler might sleep and retry when it encounters a temporary error and give up otherwise. </p> -<pre><!--{{code "progs/error.go" `/func netError/` `/STOP/`}} ---> if nerr, ok := err.(net.Error); ok && nerr.Temporary() { - time.Sleep(1e9) - continue - } - if err != nil { - log.Fatal(err) - }</pre> +{{code "/doc/progs/error.go" `/func netError/` `/STOP/`}} <p> <b>Simplifying repetitive error handling</b> @@ -244,23 +185,7 @@ application with an HTTP handler that retrieves a record from the datastore and formats it with a template. </p> -<pre><!--{{code "progs/error2.go" `/func init/` `/STOP/`}} --->func init() { - http.HandleFunc("/view", viewRecord) -} - -func viewRecord(w http.ResponseWriter, r *http.Request) { - c := appengine.NewContext(r) - key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil) - record := new(Record) - if err := datastore.Get(c, key, record); err != nil { - http.Error(w, err.Error(), 500) - return - } - if err := viewTemplate.Execute(w, record); err != nil { - http.Error(w, err.Error(), 500) - } -}</pre> +{{code "/doc/progs/error2.go" `/func init/` `/STOP/`}} <p> This function handles errors returned by the <code>datastore.Get</code> @@ -276,23 +201,13 @@ To reduce the repetition we can define our own HTTP <code>appHandler</code> type that includes an <code>error</code> return value: </p> -<pre><!--{{code "progs/error3.go" `/type appHandler/`}} --->type appHandler func(http.ResponseWriter, *http.Request) error</pre> +{{code "/doc/progs/error3.go" `/type appHandler/`}} <p> Then we can change our <code>viewRecord</code> function to return errors: </p> -<pre><!--{{code "progs/error3.go" `/func viewRecord/` `/STOP/`}} --->func viewRecord(w http.ResponseWriter, r *http.Request) error { - c := appengine.NewContext(r) - key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil) - record := new(Record) - if err := datastore.Get(c, key, record); err != nil { - return err - } - return viewTemplate.Execute(w, record) -}</pre> +{{code "/doc/progs/error3.go" `/func viewRecord/` `/STOP/`}} <p> This is simpler than the original version, but the <a @@ -302,12 +217,7 @@ To fix this we can implement the <code>http.Handler</code> interface's <code>ServeHTTP</code> method on <code>appHandler</code>: </p> -<pre><!--{{code "progs/error3.go" `/ServeHTTP/` `/STOP/`}} --->func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if err := fn(w, r); err != nil { - http.Error(w, err.Error(), 500) - } -}</pre> +{{code "/doc/progs/error3.go" `/ServeHTTP/` `/STOP/`}} <p> The <code>ServeHTTP</code> method calls the <code>appHandler</code> function @@ -323,10 +233,7 @@ Now when registering <code>viewRecord</code> with the http package we use the <code>http.HandlerFunc</code>). </p> -<pre><!--{{code "progs/error3.go" `/func init/` `/STOP/`}} --->func init() { - http.Handle("/view", appHandler(viewRecord)) -}</pre> +{{code "/doc/progs/error3.go" `/func init/` `/STOP/`}} <p> With this basic error handling infrastructure in place, we can make it more @@ -341,24 +248,19 @@ To do this we create an <code>appError</code> struct containing an <code>error</code> and some other fields: </p> -<pre><!--{{code "progs/error4.go" `/type appError/` `/STOP/`}} --->type appError struct { - Error error - Message string - Code int -}</pre> +{{code "/doc/progs/error4.go" `/type appError/` `/STOP/`}} <p> Next we modify the appHandler type to return <code>*appError</code> values: </p> -<pre><!--{{code "progs/error4.go" `/type appHandler/`}} --->type appHandler func(http.ResponseWriter, *http.Request) *appError</pre> +{{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 to be discussed in another article, but -it's the right thing to do here because <code>ServeHTTP</code> is the only +<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> @@ -369,33 +271,14 @@ status <code>Code</code> and log the full <code>Error</code> to the developer console: </p> -<pre><!--{{code "progs/error4.go" `/ServeHTTP/` `/STOP/`}} --->func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if e := fn(w, r); e != nil { // e is *appError, not os.Error. - c := appengine.NewContext(r) - c.Errorf("%v", e.Error) - http.Error(w, e.Message, e.Code) - } -}</pre> +{{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> -<pre><!--{{code "progs/error4.go" `/func viewRecord/` `/STOP/`}} --->func viewRecord(w http.ResponseWriter, r *http.Request) *appError { - c := appengine.NewContext(r) - key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil) - record := new(Record) - if err := datastore.Get(c, key, record); err != nil { - return &appError{err, "Record not found", 404} - } - if err := viewTemplate.Execute(w, record); err != nil { - return &appError{err, "Can't display record", 500} - } - return nil -}</pre> +{{code "/doc/progs/error4.go" `/func viewRecord/` `/STOP/`}} <p> This version of <code>viewRecord</code> is the same length as the original, but |