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