Commit 49d82b4c authored by Andrew Gerrand's avatar Andrew Gerrand

doc: add Defer, Panic, and Recover article

Originally published on The Go Programming Language Blog, August 4 2010.

http://blog.golang.org/2010/08/defer-panic-and-recover.html

Update #2547

R=golang-dev, r, r
CC=golang-dev
https://golang.org/cl/5479053
parent e505c9cc
......@@ -8,7 +8,7 @@ TARG=tmpltohtml
GOFILES=\
tmpltohtml.go\
all: tmpltohtml go_tutorial.html effective_go.html go1.html
all: tmpltohtml go_tutorial.html effective_go.html go1.html articles/defer_panic_recover.html
%.html: %.tmpl tmpltohtml
./makehtml $*.tmpl
......
<!-- Defer, Panic, and Recover -->
<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>
<pre><!--{{code "progs/defer.go" `/func CopyFile/` `/STOP/`}}
-->func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
</pre>
<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>
<pre><!--{{code "progs/defer2.go" `/func CopyFile/` `/STOP/`}}
-->func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
</pre>
<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>
<pre><!--{{code "progs/defer.go" `/func a/` `/STOP/`}}
-->func a() {
i := 0
defer fmt.Println(i)
i++
return
}
</pre>
<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>
<pre><!--{{code "progs/defer.go" `/func b/` `/STOP/`}}
-->func b() {
for i := 0; i &lt; 4; i++ {
defer fmt.Print(i)
}
}
</pre>
<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>
<pre><!--{{code "progs/defer.go" `/func c/` `/STOP/`}}
-->func c() (i int) {
defer func() { i++ }()
return 1
}
</pre>
<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>
<pre><!--{{code "progs/defer2.go" `/package main/` `/STOP/`}}
-->package main
import &#34;fmt&#34;
func main() {
f()
fmt.Println(&#34;Returned normally from f.&#34;)
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println(&#34;Recovered in f&#34;, r)
}
}()
fmt.Println(&#34;Calling g.&#34;)
g(0)
fmt.Println(&#34;Returned normally from g.&#34;)
}
func g(i int) {
if i &gt; 3 {
fmt.Println(&#34;Panicking!&#34;)
panic(fmt.Sprintf(&#34;%v&#34;, i))
}
defer fmt.Println(&#34;Defer in g&#34;, i)
fmt.Println(&#34;Printing in g&#34;, i)
g(i + 1)
}
</pre>
<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>
<!-- Defer, Panic, and Recover -->
<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>
......@@ -6,7 +6,7 @@
set -e
TMPL=${1:-go_tutorial.tmpl} # input file
HTML=$(basename $TMPL .tmpl).html # output file (basename)
HTML=$(dirname $TMPL)/$(basename $TMPL .tmpl).html # output file
if ! test -w $HTML
then
......
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the code snippets included in "Defer, Panic, an Recover."
package main
import (
"fmt"
"io"
"os"
)
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
// STOP OMIT
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
// STOP OMIT
func c() (i int) {
defer func() { i++ }()
return 1
}
// STOP OMIT
// Intial version.
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
// STOP OMIT
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the code snippets included in "Defer, Panic, an Recover."
package main
import "fmt"
import "io" // OMIT
import "os" // OMIT
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
// STOP OMIT
// Revised version.
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
// STOP OMIT
......@@ -20,25 +20,41 @@ else
$GC file.go
fi
defer_panic_recover="
defer.go
defer2.go
"
effective_go="
eff_bytesize.go
eff_qr.go
eff_sequence.go
"
go_tutorial="
cat.go
cat_rot13.go
echo.go
file.go
helloworld.go
helloworld3.go
print.go
print_string.go
server.go
server1.go
sieve.go
sieve1.go
sort.go
sortmain.go
strings.go
sum.go
"
for i in \
helloworld.go \
helloworld3.go \
echo.go \
cat.go \
cat_rot13.go \
sum.go \
sort.go \
sortmain.go \
print.go \
print_string.go \
sieve.go \
sieve1.go \
server1.go \
strings.go \
eff_bytesize.go\
eff_qr.go \
eff_sequence.go\
go1.go\
$defer_panic_recover \
$effective_go \
$go_tutorial \
go1.go \
; do
$GC $i
done
......
......@@ -31,6 +31,7 @@ import (
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
......@@ -54,8 +55,8 @@ func main() {
}
// Read and parse the input.
name := flag.Args()[0]
tmpl := template.New(name).Funcs(templateFuncs)
name := flag.Arg(0)
tmpl := template.New(filepath.Base(name)).Funcs(templateFuncs)
if _, err := tmpl.ParseFiles(name); err != nil {
log.Fatal(err)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment