github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/defer-panic-and-recover.article (about)

     1  Defer, Panic, and Recover
     2  4 Aug 2010
     3  Tags: defer, panic, recover, technical, function
     4  
     5  Andrew Gerrand
     6  
     7  * Introduction
     8  
     9  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.
    10  
    11   
    12  A *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.
    13  
    14   
    15  For example, let's look at a function that opens two files and copies the contents of one file to the other:
    16  
    17   
    18  	func CopyFile(dstName, srcName string) (written int64, err error) {
    19  	    src, err := os.Open(srcName)
    20  	    if err != nil {
    21  	        return
    22  	    }
    23  
    24  	    dst, err := os.Create(dstName)
    25  	    if err != nil {
    26  	        return
    27  	    }
    28  
    29  	    written, err = io.Copy(dst, src)
    30  	    dst.Close()
    31  	    src.Close()
    32  	    return
    33  	}
    34  
    35  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:
    36  
    37   
    38  	func CopyFile(dstName, srcName string) (written int64, err error) {
    39  	    src, err := os.Open(srcName)
    40  	    if err != nil {
    41  	        return
    42  	    }
    43  	    defer src.Close()
    44  
    45  	    dst, err := os.Create(dstName)
    46  	    if err != nil {
    47  	        return
    48  	    }
    49  	    defer dst.Close()
    50  
    51  	    return io.Copy(dst, src)
    52  	}
    53  
    54  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 _will_ be closed.
    55  
    56  The behavior of defer statements is straightforward and predictable. There are three simple rules:
    57   
    58  1. _A_deferred_function's_arguments_are_evaluated_when_the_defer_statement_is_evaluated._
    59   
    60  In this example, the expression "i" is evaluated when the Println call is deferred. The deferred call will print "0" after the function returns.
    61  
    62   
    63  	func a() {
    64  	    i := 0
    65  	    defer fmt.Println(i)
    66  	    i++
    67  	    return
    68  	}
    69  
    70  2. _Deferred_function_calls_are_executed_in_Last_In_First_Out_order_after_the_surrounding_function_returns._
    71   
    72  This function prints "3210":
    73  
    74  	func b() {
    75  	    for i := 0; i < 4; i++ {
    76  	        defer fmt.Print(i)
    77  	    }
    78  	}
    79  
    80   
    81  3. _Deferred_functions_may_read_and_assign_to_the_returning_function's_named_return_values._
    82   
    83  In this example, a deferred function increments the return value i _after_ the surrounding function returns. Thus, this function returns 2:
    84  
    85  	func c() (i int) {
    86  	    defer func() { i++ }()
    87  	    return 1
    88  	}
    89  
    90   
    91  This is convenient for modifying the error return value of a function; we will see an example of this shortly.
    92  
    93   
    94  *Panic* is a built-in function that stops the ordinary flow of control and begins _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.
    95  
    96   
    97  *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.
    98  
    99   
   100  Here's an example program that demonstrates the mechanics of panic and defer:
   101  
   102  	package main
   103  
   104  	import "fmt"
   105  
   106  	func main() {
   107  	    f()
   108  	    fmt.Println("Returned normally from f.")
   109  	}
   110  
   111  	func f() {
   112  	    defer func() {
   113  	        if r := recover(); r != nil {
   114  	            fmt.Println("Recovered in f", r)
   115  	        }
   116  	    }()
   117  	    fmt.Println("Calling g.")
   118  	    g(0)
   119  	    fmt.Println("Returned normally from g.")
   120  	}
   121  
   122  	func g(i int) {
   123  	    if i > 3 {
   124  	        fmt.Println("Panicking!")
   125  	        panic(fmt.Sprintf("%v", i))
   126  	    }
   127  	    defer fmt.Println("Defer in g", i)
   128  	    fmt.Println("Printing in g", i)
   129  	    g(i + 1)
   130  	}
   131  
   132   
   133  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.
   134  
   135   
   136  The program will output:
   137  
   138   
   139  	Calling g.
   140  	Printing in g 0
   141  	Printing in g 1
   142  	Printing in g 2
   143  	Printing in g 3
   144  	Panicking!
   145  	Defer in g 3
   146  	Defer in g 2
   147  	Defer in g 1
   148  	Defer in g 0
   149  	Recovered in f 4
   150  	Returned normally from f.
   151  
   152   
   153  
   154  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:
   155  
   156   
   157  	Calling g.
   158  	Printing in g 0
   159  	Printing in g 1
   160  	Printing in g 2
   161  	Printing in g 3
   162  	Panicking!
   163  	Defer in g 3
   164  	Defer in g 2
   165  	Defer in g 1
   166  	Defer in g 0
   167  	panic: 4
   168  	 
   169  	panic PC=0x2a9cd8
   170  	[stack trace omitted]
   171  
   172   
   173  
   174  For a real-world example of *panic* and *recover*, see the [[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 [[http://golang.org/src/pkg/encoding/json/decode.go][decode.go]]).
   175  
   176  The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.
   177  
   178   
   179  Other uses of *defer* (beyond the file.Close example given earlier) include releasing a mutex:
   180  
   181  	mu.Lock()
   182  	defer mu.Unlock()
   183  
   184   
   185  
   186  printing a footer:
   187  
   188   
   189  	printHeader()
   190  	defer printFooter()
   191  
   192   
   193  
   194  and more.
   195  
   196   
   197  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.