diff --git a/doc/effective_go.html b/doc/effective_go.html index 78eadbd7bad..86c24664f31 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -29,7 +29,7 @@ and the tutorial, both of which you should read first.

-

Examples

+

Examples

The Go package sources @@ -2569,10 +2569,175 @@ for try := 0; try < 2; try++ { } -

Panic and recover

+

Panic

-TODO: Short discussion of panic and recover goes here. +The usual way to report an error to a caller is to return an +os.Error as an extra return value. The canonical +Read method is a well-known instance; it returns a byte +count and an os.Error. But what if the error is +unrecoverable? Sometimes the program simply cannot continue. +

+ +

+For this purpose, there is a built-in function panic +that in effect creates a run-time error that will stop the program +(but see the next section). The function takes a single argument +of arbitrary type—often a string—to be printed as the +program dies. It's also a way to indicate that something impossible has +happened, such as exiting an infinite loop. In fact, the compiler +recognizes a panic at the end of a function and +suppresses the usual check for a return statement. +

+ + +
+// A toy implementation of cube root using Newton's method.
+func CubeRoot(x float64) float64 {
+    z := x/3   // Arbitrary intitial value
+    for i := 0; i < 1e6; i++ {
+        prevz := z
+        z -= (z*z*z-x) / (3*z*z)
+        if veryClose(z, prevz) {
+            return z
+        }
+    }
+    // A million iterations has not converged; something is wrong.
+    panic(fmt.Sprintf("CubeRoot(%g) did not converge", x)
+}
+
+ +

+This is only an example but real library functions should +avoid panic. If the problem can be masked or worked +around, it's always better to let things continue to run rather +than taking down the whole program. One possible counterexample +is during initialization: if the library truly cannot set itself up, +it might be reasonable to panic, so to speak. +

+ +
+var user = os.Getenv("USER")
+
+func init() {
+    if user == "" {
+        panic("no value for $USER")
+    }
+}
+
+ +

Recover

+ +

+When panic is called, including implicitly for run-time +errors such indexing an array out of bounds or failing a type +assertion, it immediately stops execution of the current function +and begins unwinding the stack of the goroutine, running any deferred +functions along the way. If that unwinding reaches the top of the +goroutine's stack, the program dies. However, it is possible to +use the built-in function recover to regain control +of the goroutine and resume normal execution. +

+ +

+A call to recover stops the unwinding and returns the +argument passed to panic. Because the only code that +runs while unwinding is inside deferred functions, recover +is only useful inside deferred functions. +

+ +

+One application of recover is to shut down a failing goroutine +inside a server without killing the other executing goroutines. +

+ +
+func server(workChan <-chan *Work) {
+    for work := range workChan {
+        safelyDo(work)
+    }
+}
+
+func safelyDo(work *Work) {
+    defer func() {
+        if err := recover(); err != nil {
+            log.Stderr("work failed:", err)
+        }
+    }()
+    do(work)
+}
+
+ +

+In this example, if do(work) panics, the result will be +logged and the goroutine will exit cleanly without disturbing the +others. There's no need to do anything else in the deferred closure; +calling recover handles the condition completely. +

+ +

+Note that with this recovery pattern in place, the do +function (and anything it calls) can get out of any bad situation +cleanly by calling panic. We can use that idea to +simplify error handling in complex software. Let's look at an +idealized excerpt from the regexp package, which reports +parsing errors by calling panic with a local +Error type. Here's the definition of Error, +an error method, and the Compile function. +

+ +
+// Error is the type of a parse error; it satisfies os.Error.
+type Error string
+func (e Error) String() string {
+    return string(e)
+}
+
+// error is a method of *Regexp that reports parsing errors by
+// panicking with an Error.
+func (regexp *Regexp) error(err string) {
+    panic(Error(err))
+}
+
+// Compile returns a parsed representation of the regular expression.
+func Compile(str string) (regexp *Regexp, err os.Error) {
+    regexp = new(Regexp)
+    // doParse will panic if there is a parse error.
+    defer func() {
+        if e := recover(); e != nil {
+            regexp = nil    // Clear return value.
+            err = e.(Error) // Will re-panic if not a parse error.
+        }
+    }()
+    return regexp.doParse(str), nil
+}
+
+ +

+If doParse panics, the recovery block will set the +return value to nil—deferred functions can modify +named return values. It then will then check, in the assignment +to err, that the problem was a parse error by asserting +that it has type Error. +If it does not, the type assertion will fail, causing a run-time error +that continues the stack unwinding as though nothing had interrupted +it. This check means that if something unexpected happens, such +as an array index out of bounds, the code will fail even though we +are using panic and recover to handle +user-triggered errors. +

+ +

+With this error handling in place, the error method +makes it easy to report parse errors without worrying about unwinding +the parse stack by hand. +

+ +

+Useful though this pattern is, it should be used only within a package. +Parse turns its internal panic calls into +os.Error values; it does not expose panics +to its client. That is a good rule to follow.