summaryrefslogtreecommitdiff
path: root/doc/articles/defer_panic_recover.tmpl
diff options
context:
space:
mode:
Diffstat (limited to 'doc/articles/defer_panic_recover.tmpl')
-rw-r--r--doc/articles/defer_panic_recover.tmpl195
1 files changed, 195 insertions, 0 deletions
diff --git a/doc/articles/defer_panic_recover.tmpl b/doc/articles/defer_panic_recover.tmpl
new file mode 100644
index 000000000..60c8eebe0
--- /dev/null
+++ b/doc/articles/defer_panic_recover.tmpl
@@ -0,0 +1,195 @@
+<!--{
+ "Title": "Defer, Panic, and Recover"
+}-->
+{{donotedit}}
+<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 "progs/defer.go" `/func CopyFile/` `/STOP/`}}
+
+<p>
+This works, but there is a bug. If the second call to os.Open 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 "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 "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 "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 "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 "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 is 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>