-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdefer.tex
More file actions
263 lines (215 loc) · 7.01 KB
/
defer.tex
File metadata and controls
263 lines (215 loc) · 7.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
\cleardoublepage
\phantomsection
\addcontentsline{toc}{chapter}{Defer, Panic, and Recover}
\chapter*{Defer, Panic and Recover}
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.
A \textbf{defer statement} 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.
For example, let's look at a function that opens two files and copies
the contents of one file to the other:
\begin{Verbatim}[frame=single]
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
}
\end{Verbatim}
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:
\begin{Verbatim}[frame=single]
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)
}
\end{Verbatim}
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 \emph{will} be closed.
The behavior of defer statements is straightforward and predictable.
There are three simple rules:
1. \emph{A deferred function's arguments are evaluated when the defer
statement is evaluated.}
In this example, the expression ``i'' is evaluated when the Println call
is deferred. The deferred call will print ``0'' after the function
returns.
\begin{Verbatim}[frame=single]
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
\end{Verbatim}
2. \emph{Deferred function calls are executed in Last In First Out
order}after\emph{the surrounding function returns.}
This function prints ``3210'':
\begin{Verbatim}[frame=single]
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
\end{Verbatim}
3. \emph{Deferred functions may read and assign to the returning
function's named return values.}
In this example, a deferred function increments the return value i
\emph{after} the surrounding function returns. Thus, this function
returns 2:
\begin{Verbatim}[frame=single]
func c() (i int) {
defer func() { i++ }()
return 1
}
\end{Verbatim}
This is convenient for modifying the error return value of a function;
we will see an example of this shortly.
\textbf{Panic} is a built-in function that stops the ordinary flow of
control and begins \emph{panicking}. 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.
\textbf{Recover} 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.
Here's an example program that demonstrates the mechanics of panic and
defer:
\begin{Verbatim}[frame=single]
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)
}
\end{Verbatim}
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.
The program will output:
\begin{Verbatim}[frame=single]
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.
\end{Verbatim}
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:
\begin{Verbatim}[frame=single]
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]
\end{Verbatim}
For a real-world example of \textbf{panic} and \textbf{recover}, see the
\href{http://golang.org/pkg/encoding/json/}{json package} 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'
methods of the decodeState type in
\href{http://golang.org/src/pkg/encoding/json/decode.go}{decode.go}).
The convention in the Go libraries is that even when a package uses
panic internally, its external API still presents explicit error return
values.
Other uses of \textbf{defer} (beyond the file.Close example given
earlier) include releasing a mutex:
\begin{Verbatim}[frame=single]
mu.Lock()
defer mu.Unlock()
\end{Verbatim}
printing a footer:
\begin{Verbatim}[frame=single]
printHeader()
defer printFooter()
\end{Verbatim}
and more.
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.