diff options
Diffstat (limited to 'doc/articles/defer_panic_recover.tmpl')
-rw-r--r-- | doc/articles/defer_panic_recover.tmpl | 195 |
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> |