summaryrefslogtreecommitdiff
path: root/doc/articles/error_handling.html
diff options
context:
space:
mode:
Diffstat (limited to 'doc/articles/error_handling.html')
-rw-r--r--doc/articles/error_handling.html167
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(&#34;filename.ext&#34;)
- 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 &amp;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 &lt; 0 {
- return 0, errors.New(&#34;math: square root of negative number&#34;)
- }
- // 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 &lt; 0 {
- return 0, fmt.Errorf(&#34;math: square root of negative number %g&#34;, 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(&#34;math: square root of negative number %g&#34;, 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(&amp;val); err != nil {
- if serr, ok := err.(*json.SyntaxError); ok {
- line, col := findLine(f, serr.Offset)
- return fmt.Errorf(&#34;%s:%d:%d: %v&#34;, 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 &amp;&amp; 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(&#34;/view&#34;, viewRecord)
-}
-
-func viewRecord(w http.ResponseWriter, r *http.Request) {
- c := appengine.NewContext(r)
- key := datastore.NewKey(c, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 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, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 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(&#34;/view&#34;, 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(&#34;%v&#34;, 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, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 0, nil)
- record := new(Record)
- if err := datastore.Get(c, key, record); err != nil {
- return &amp;appError{err, &#34;Record not found&#34;, 404}
- }
- if err := viewTemplate.Execute(w, record); err != nil {
- return &amp;appError{err, &#34;Can&#39;t display record&#34;, 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