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. +
+ +