From 519725bb3c075ee2462c929f5997cb068e18466a Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Mon, 26 Mar 2012 16:50:58 +0200 Subject: Imported Upstream version 2012.03.22 --- doc/articles/error_handling.html | 167 ++++++--------------------------------- 1 file changed, 25 insertions(+), 142 deletions(-) (limited to 'doc/articles/error_handling.html') 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 @@ -

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 os.Open function returns a non-nil error value when it fails to open a file.

-
func Open(name string) (file *File, err error)
+{{code "/doc/progs/error.go" `/func Open/`}}

The following code uses os.Open to open a file. If an error occurs it calls log.Fatal to print the error message and stop.

-
    f, err := os.Open("filename.ext")
-    if err != nil {
-        log.Fatal(err)
-    }
-    // do something with the open *File f
+{{code "/doc/progs/error.go" `/func openFile/` `/STOP/`}}

You can get a lot done in Go knowing just this about the error @@ -59,15 +50,7 @@ The most commonly-used error implementation is the errors package's unexported errorString type.

-
// errorString is a trivial implementation of error.
-type errorString struct {
-    s string
-}
-
-func (e *errorString) Error() string {
-    return e.s
-}
+{{code "/doc/progs/error.go" `/errorString/` `/STOP/`}}

You can construct one of these values with the errors.New @@ -75,23 +58,13 @@ function. It takes a string that it converts to an errors.errorStringerror value.

-
// New returns an error that formats as the given text.
-func New(text string) error {
-    return &errorString{text}
-}
+{{code "/doc/progs/error.go" `/New/` `/STOP/`}}

Here's how you might use errors.New:

-
func Sqrt(f float64) (float64, error) {
-    if f < 0 {
-        return 0, errors.New("math: square root of negative number")
-    }
-    // implementation
-}
+{{code "/doc/progs/error.go" `/func Sqrt/` `/STOP/`}}

A caller passing a negative argument to Sqrt receives a non-nil @@ -101,11 +74,7 @@ A caller passing a negative argument to Sqrt receives a non-nil Error method, or by just printing it:

-
    f, err := Sqrt(-1)
-    if err != nil {
-        fmt.Println(err)
-    }
+{{code "/doc/progs/error.go" `/func printErr/` `/STOP/`}}

The fmt package formats an error value @@ -126,10 +95,7 @@ rules and returns it as an error created by errors.New.

-
    if f < 0 {
-        return 0, fmt.Errorf("math: square root of negative number %g", f)
-    }
+{{code "/doc/progs/error.go" `/fmtError/` `/STOP/`}}

In many cases fmt.Errorf is good enough, but since @@ -143,12 +109,7 @@ argument passed to Sqrt. We can enable that by defining a new error implementation instead of using errors.errorString:

-
type NegativeSqrtError float64
-
-func (f NegativeSqrtError) Error() string {
-    return fmt.Sprintf("math: square root of negative number %g", float64(f))
-}
+{{code "/doc/progs/error.go" `/type NegativeSqrtError/` `/STOP/`}}

A sophisticated caller can then use a @@ -164,13 +125,7 @@ As another example, the json package specifies returns when it encounters a syntax error parsing a JSON blob.

-
type SyntaxError struct {
-    msg    string // description of error
-    Offset int64  // error occurred after reading Offset bytes
-}
-
-func (e *SyntaxError) Error() string { return e.msg }
+{{code "/doc/progs/error.go" `/type SyntaxError/` `/STOP/`}}

The Offset 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:

-
    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
-    }
+{{code "/doc/progs/error.go" `/func decodeError/` `/STOP/`}}

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

-
        if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
-            time.Sleep(1e9)
-            continue
-        }
-        if err != nil {
-            log.Fatal(err)
-        }
+{{code "/doc/progs/error.go" `/func netError/` `/STOP/`}}

Simplifying repetitive error handling @@ -244,23 +185,7 @@ application with an HTTP handler that retrieves a record from the datastore and formats it with a template.

-
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)
-    }
-}
+{{code "/doc/progs/error2.go" `/func init/` `/STOP/`}}

This function handles errors returned by the datastore.Get @@ -276,23 +201,13 @@ To reduce the repetition we can define our own HTTP appHandler type that includes an error return value:

-
type appHandler func(http.ResponseWriter, *http.Request) error
+{{code "/doc/progs/error3.go" `/type appHandler/`}}

Then we can change our viewRecord function to return errors:

-
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)
-}
+{{code "/doc/progs/error3.go" `/func viewRecord/` `/STOP/`}}

This is simpler than the original version, but the http.Handler interface's ServeHTTP method on appHandler:

-
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-    if err := fn(w, r); err != nil {
-        http.Error(w, err.Error(), 500)
-    }
-}
+{{code "/doc/progs/error3.go" `/ServeHTTP/` `/STOP/`}}

The ServeHTTP method calls the appHandler function @@ -323,10 +233,7 @@ Now when registering viewRecord with the http package we use the http.HandlerFunc).

-
func init() {
-    http.Handle("/view", appHandler(viewRecord))
-}
+{{code "/doc/progs/error3.go" `/func init/` `/STOP/`}}

With this basic error handling infrastructure in place, we can make it more @@ -341,24 +248,19 @@ To do this we create an appError struct containing an error and some other fields:

-
type appError struct {
-    Error   error
-    Message string
-    Code    int
-}
+{{code "/doc/progs/error4.go" `/type appError/` `/STOP/`}}

Next we modify the appHandler type to return *appError values:

-
type appHandler func(http.ResponseWriter, *http.Request) *appError
+{{code "/doc/progs/error4.go" `/type appHandler/`}}

(It's usually a mistake to pass back the concrete type of an error rather than -error, for reasons to be discussed in another article, but -it's the right thing to do here because ServeHTTP is the only +error, +for reasons discussed in the Go FAQ, +but it's the right thing to do here because ServeHTTP is the only place that sees the value and uses its contents.)

@@ -369,33 +271,14 @@ status Code and log the full Error to the developer console:

-
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)
-    }
-}
+{{code "/doc/progs/error4.go" `/ServeHTTP/` `/STOP/`}}

Finally, we update viewRecord to the new function signature and have it return more context when it encounters an error:

-
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
-}
+{{code "/doc/progs/error4.go" `/func viewRecord/` `/STOP/`}}

This version of viewRecord is the same length as the original, but -- cgit v1.2.3