mirror of
https://github.com/golang/go
synced 2024-11-24 04:20:03 -07:00
Effective Go: panic and recover
R=rsc, iant CC=golang-dev https://golang.org/cl/1718042
This commit is contained in:
parent
050905b985
commit
99b23a1e5b
@ -29,7 +29,7 @@ and the <a href="go_tutorial.html">tutorial</a>, both of which you
|
||||
should read first.
|
||||
</p>
|
||||
|
||||
<h3 id="read">Examples</h3>
|
||||
<h3 id="examples">Examples</h3>
|
||||
|
||||
<p>
|
||||
The <a href="/src/pkg/">Go package sources</a>
|
||||
@ -2569,10 +2569,175 @@ for try := 0; try < 2; try++ {
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3 id="panic_recover">Panic and recover</h3>
|
||||
<h3 id="panic">Panic</h3>
|
||||
|
||||
<p>
|
||||
TODO: Short discussion of panic and recover goes here.
|
||||
The usual way to report an error to a caller is to return an
|
||||
<code>os.Error</code> as an extra return value. The canonical
|
||||
<code>Read</code> method is a well-known instance; it returns a byte
|
||||
count and an <code>os.Error</code>. But what if the error is
|
||||
unrecoverable? Sometimes the program simply cannot continue.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For this purpose, there is a built-in function <code>panic</code>
|
||||
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 <code>panic</code> at the end of a function and
|
||||
suppresses the usual check for a <code>return</code> statement.
|
||||
</p>
|
||||
|
||||
|
||||
<pre>
|
||||
// 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)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
This is only an example but real library functions should
|
||||
avoid <code>panic</code>. 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.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var user = os.Getenv("USER")
|
||||
|
||||
func init() {
|
||||
if user == "" {
|
||||
panic("no value for $USER")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3 id="recover">Recover</h3>
|
||||
|
||||
<p>
|
||||
When <code>panic</code> 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 <code>recover</code> to regain control
|
||||
of the goroutine and resume normal execution.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A call to <code>recover</code> stops the unwinding and returns the
|
||||
argument passed to <code>panic</code>. Because the only code that
|
||||
runs while unwinding is inside deferred functions, <code>recover</code>
|
||||
is only useful inside deferred functions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
One application of <code>recover</code> is to shut down a failing goroutine
|
||||
inside a server without killing the other executing goroutines.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
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)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
In this example, if <code>do(work)</code> 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 <code>recover</code> handles the condition completely.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that with this recovery pattern in place, the <code>do</code>
|
||||
function (and anything it calls) can get out of any bad situation
|
||||
cleanly by calling <code>panic</code>. We can use that idea to
|
||||
simplify error handling in complex software. Let's look at an
|
||||
idealized excerpt from the <code>regexp</code> package, which reports
|
||||
parsing errors by calling <code>panic</code> with a local
|
||||
<code>Error</code> type. Here's the definition of <code>Error</code>,
|
||||
an <code>error</code> method, and the <code>Compile</code> function.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
// 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
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If <code>doParse</code> panics, the recovery block will set the
|
||||
return value to <code>nil</code>—deferred functions can modify
|
||||
named return values. It then will then check, in the assignment
|
||||
to <code>err</code>, that the problem was a parse error by asserting
|
||||
that it has type <code>Error</code>.
|
||||
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 <code>panic</code> and <code>recover</code> to handle
|
||||
user-triggered errors.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
With this error handling in place, the <code>error</code> method
|
||||
makes it easy to report parse errors without worrying about unwinding
|
||||
the parse stack by hand.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Useful though this pattern is, it should be used only within a package.
|
||||
<code>Parse</code> turns its internal <code>panic</code> calls into
|
||||
<code>os.Error</code> values; it does not expose <code>panics</code>
|
||||
to its client. That is a good rule to follow.
|
||||
</p>
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user