mirror of
https://github.com/golang/go
synced 2024-11-24 23:17:57 -07:00
a40065ac68
doc: convert to use godoc built-in templates tmpltohtml is gone, to avoid having a second copy of the code. Instead, godoc -url /doc/go1.html will print the actual HTML served for that URL. "make" will generate files named go1.rawhtml etc, which can be fed through tidy. It can be hard to tell from the codereview diffs, but all the tmpl files have been renamed to be html files and then have "Template": true added. R=golang-dev, adg, r, gri CC=golang-dev https://golang.org/cl/5782046
197 lines
5.5 KiB
HTML
197 lines
5.5 KiB
HTML
<!--{
|
|
"Title": "Defer, Panic, and Recover",
|
|
"Template": true
|
|
}-->
|
|
|
|
<p>
|
|
Go has the usual mechanisms for control flow: if, for, switch, goto. It also
|
|
has the go statement to run code in a separate goroutine. Here I'd like to
|
|
discuss some of the less common ones: defer, panic, and recover.
|
|
</p>
|
|
|
|
<p>
|
|
A <b>defer statement</b> pushes a function call onto a list. The list of saved
|
|
calls is executed after the surrounding function returns. Defer is commonly
|
|
used to simplify functions that perform various clean-up actions.
|
|
</p>
|
|
|
|
<p>
|
|
For example, let's look at a function that opens two files and copies the
|
|
contents of one file to the other:
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer.go" `/func CopyFile/` `/STOP/`}}
|
|
|
|
<p>
|
|
This works, but there is a bug. If the call to os.Create fails, the
|
|
function will return without closing the source file. This can be easily
|
|
remedied by putting a call to src.Close() before the second return statement,
|
|
but if the function were more complex the problem might not be so easily
|
|
noticed and resolved. By introducing defer statements we can ensure that the
|
|
files are always closed:
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer2.go" `/func CopyFile/` `/STOP/`}}
|
|
|
|
<p>
|
|
Defer statements allow us to think about closing each file right after opening
|
|
it, guaranteeing that, regardless of the number of return statements in the
|
|
function, the files <i>will</i> be closed.
|
|
</p>
|
|
|
|
<p>
|
|
The behavior of defer statements is straightforward and predictable. There are
|
|
three simple rules:
|
|
</p>
|
|
|
|
<p>
|
|
1. <i>A deferred function's arguments are evaluated when the defer statement is
|
|
evaluated.</i>
|
|
</p>
|
|
|
|
<p>
|
|
In this example, the expression "i" is evaluated when the Println call is
|
|
deferred. The deferred call will print "0" after the function returns.
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer.go" `/func a/` `/STOP/`}}
|
|
|
|
<p>
|
|
2. <i>Deferred function calls are executed in Last In First Out order
|
|
</i>after<i> the surrounding function returns.</i>
|
|
</p>
|
|
|
|
<p>
|
|
This function prints "3210":
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer.go" `/func b/` `/STOP/`}}
|
|
|
|
<p>
|
|
3. <i>Deferred functions may read and assign to the returning function's named
|
|
return values.</i>
|
|
</p>
|
|
|
|
<p>
|
|
In this example, a deferred function increments the return value i <i>after</i>
|
|
the surrounding function returns. Thus, this function returns 2:
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer.go" `/func c/` `/STOP/`}}
|
|
|
|
<p>
|
|
This is convenient for modifying the error return value of a function; we will
|
|
see an example of this shortly.
|
|
</p>
|
|
|
|
<p>
|
|
<b>Panic</b> is a built-in function that stops the ordinary flow of control and
|
|
begins <i>panicking</i>. When the function F calls panic, execution of F stops,
|
|
any deferred functions in F are executed normally, and then F returns to its
|
|
caller. To the caller, F then behaves like a call to panic. The process
|
|
continues up the stack until all functions in the current goroutine have
|
|
returned, at which point the program crashes. Panics can be initiated by
|
|
invoking panic directly. They can also be caused by runtime errors, such as
|
|
out-of-bounds array accesses.
|
|
</p>
|
|
|
|
<p>
|
|
<b>Recover</b> is a built-in function that regains control of a panicking
|
|
goroutine. Recover is only useful inside deferred functions. During normal
|
|
execution, a call to recover will return nil and have no other effect. If the
|
|
current goroutine is panicking, a call to recover will capture the value given
|
|
to panic and resume normal execution.
|
|
</p>
|
|
|
|
<p>
|
|
Here's an example program that demonstrates the mechanics of panic and defer:
|
|
</p>
|
|
|
|
{{code "/doc/progs/defer2.go" `/package main/` `/STOP/`}}
|
|
|
|
<p>
|
|
The function g takes the int i, and panics if i is greater than 3, or else it
|
|
calls itself with the argument i+1. The function f defers a function that calls
|
|
recover and prints the recovered value (if it is non-nil). Try to picture what
|
|
the output of this program might be before reading on.
|
|
</p>
|
|
|
|
<p>
|
|
The program will output:
|
|
</p>
|
|
|
|
<pre>Calling g.
|
|
Printing in g 0
|
|
Printing in g 1
|
|
Printing in g 2
|
|
Printing in g 3
|
|
Panicking!
|
|
Defer in g 3
|
|
Defer in g 2
|
|
Defer in g 1
|
|
Defer in g 0
|
|
Recovered in f 4
|
|
Returned normally from f.</pre>
|
|
|
|
<p>
|
|
If we remove the deferred function from f the panic is not recovered and
|
|
reaches the top of the goroutine's call stack, terminating the program. This
|
|
modified program will output:
|
|
</p>
|
|
|
|
<pre>Calling g.
|
|
Printing in g 0
|
|
Printing in g 1
|
|
Printing in g 2
|
|
Printing in g 3
|
|
Panicking!
|
|
Defer in g 3
|
|
Defer in g 2
|
|
Defer in g 1
|
|
Defer in g 0
|
|
panic: 4
|
|
|
|
panic PC=0x2a9cd8
|
|
[stack trace omitted]</pre>
|
|
|
|
<p>
|
|
For a real-world example of <b>panic</b> and <b>recover</b>, see the
|
|
<a href="/pkg/encoding/json/">json package</a> from the Go standard library.
|
|
It decodes JSON-encoded data with a set of recursive functions.
|
|
When malformed JSON is encountered, the parser calls panic to unwind the
|
|
stack to the top-level function call, which recovers from the panic and returns
|
|
an appropriate error value (see the 'error' and 'unmarshal' functions in
|
|
<a href="/src/pkg/encoding/json/decode.go">decode.go</a>).
|
|
</p>
|
|
|
|
<p>
|
|
The convention in the Go libraries is that even when a package uses panic
|
|
internally, its external API still presents explicit error return values.
|
|
</p>
|
|
|
|
<p>
|
|
Other uses of <b>defer</b> (beyond the file.Close() example given earlier)
|
|
include releasing a mutex:
|
|
</p>
|
|
|
|
<pre>mu.Lock()
|
|
defer mu.Unlock()</pre>
|
|
|
|
<p>
|
|
printing a footer:
|
|
</p>
|
|
|
|
<pre>printHeader()
|
|
defer printFooter()</pre>
|
|
|
|
<p>
|
|
and more.
|
|
</p>
|
|
|
|
<p>
|
|
In summary, the defer statement (with or without panic and recover) provides an
|
|
unusual and powerful mechanism for control flow. It can be used to model a
|
|
number of features implemented by special-purpose structures in other
|
|
programming languages. Try it out.
|
|
</p>
|