From: Rob Pike
The Go package sources @@ -2569,10 +2569,175 @@ for try := 0; try < 2; try++ { } -
-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")
+ }
+}
+
+
+
+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.