diff --git a/doc/effective_go.html b/doc/effective_go.html index 18a3e981a9..78eadbd7ba 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -770,6 +770,139 @@ func ReadFull(r Reader, buf []byte) (n int, err os.Error) { } +
+Go's defer statement schedules a function call (the
+deferred function) to be run immediately before the function
+executing the defer returns. It's an unusual but
+effective way to deal with situations such as resources that must be
+released regardless of which path a function takes to return. The
+canonical examples are unlocking a mutex or closing a file.
+
+// Contents returns the file's contents as a string.
+func Contents(filename string) (string, os.Error) {
+ f, err := os.Open(filename, os.O_RDONLY, 0)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close() // f.Close will run when we're finished.
+
+ var result []byte
+ buf := make([]byte, 100)
+ for {
+ n, err := f.Read(buf[0:])
+ result = bytes.Add(result, buf[0:n])
+ if err != nil {
+ if err == os.EOF {
+ break
+ }
+ return "", err // f will be closed if we return here.
+ }
+ }
+ return string(result), nil // f will be closed if we return here.
+}
+
+
++Deferring a function like this has two advantages. First, it +guarantees that you will never forget to close the file, a mistake +that's easy to make if you later edit the function to add a new return +path. Second, it means that the close sits near the open, +which is much clearer than placing it at the end of the function. +
+ ++The arguments to the deferred function (which includes the receiver if +the function is a method) are evaluated when the defer +executes, not when the call executes. Besides avoiding worries +about variables changing values as the function executes, this means +that a single deferred call site can defer multiple function +executions. Here's a silly example. +
+ +
+for i := 0; i < 5; i++ {
+ defer fmt.Printf("%d ", i)
+}
+
+
+
+Deferred functions are executed in LIFO order, so this code will cause
+4 3 2 1 0 to be printed when the function returns. A
+more plausible example is a simple way to trace function execution
+through the program. We could write a couple of simple tracing
+routines like this:
+
+func trace(s string) { fmt.Println("entering:", s) }
+func untrace(s string) { fmt.Println("leaving:", s) }
+
+// Use them like this:
+func a() {
+ trace("a")
+ defer untrace("a")
+ // do something....
+}
+
+
+
+We can do better by exploiting the fact that arguments to deferred
+functions are evaluated when the defer executes. The
+tracing routine can set up the argument to the untracing routine.
+This example:
+
+func trace(s string) string {
+ fmt.Println("entering:", s)
+ return s
+}
+
+func un(s string) {
+ fmt.Println("leaving:", s)
+}
+
+func a() {
+ defer un(trace("a"))
+ fmt.Println("in a")
+}
+
+func b() {
+ defer un(trace("b"))
+ fmt.Println("in b")
+ a()
+}
+
+func main() {
+ b()
+}
+
+
++prints +
+ ++entering: b +in b +entering: a +in a +leaving: a +leaving: b ++ +
+For programmers accustomed to block-level resource management from
+other languages, defer may seem peculiar, but its most
+interesting and powerful applications come precisely from the fact
+that it's not block-based but function based. In the section on
+panic and recover we'll see an example.
+
new()+TODO: Short discussion of panic and recover goes here. +
+ +