Go supports the notion of deferring a function call. Placing the keyword defer
before a function call has the interesting effect of pushing the function unto an internal stack, delaying its execution right before the enclosing function returns. To better explain this, let us start with the following simple program that illustrates the use of defer
:
package main import "fmt" func do(steps ...string) { defer fmt.Println("All done!") for _, s := range steps { defer fmt.Println(s) } fmt.Println("Starting") } func main() { do( "Find key", "Aplly break", "Put key in ignition", "Start car", ) }
golang.fyi/ch05/defer1.go
The previous example defines the do
function that takes variadic parameter steps
. The function defers the statement with defer fmt.Println("All done!")
. Next, the function loops through slice steps
and defers the output of each element with defer fmt.Println(s).
The last statement in the function do
is a non-deferred call to fmt.Println("Starting").
Notice the order of the printed string values when the program is executed, as shown in the following output:
$> go run defer1.go Starting Start car Put key in ignition Aplly break Find key All done!
There are a couple facts that explain the reverse order of the printout. First, recall that deferred functions are executed right before their enclosing function returns. Therefore, the first value printed is generated by the last non-deferred method call. Next, as stated earlier, deferred statements are pushed into a stack. Therefore, deferred calls are executed using a last-in-first-out order. That is why "All done!"
is the last string value printed in the output.
The defer
keyword modifies the execution flow of a program by delaying function calls. One idiomatic usage for this feature is to do a resource cleanup. Since defer will always get executed when the surrounding function returns, it is a good place to attach cleanup code such as:
To illustrate, let us return to our anagram example from earlier. The following code snippet shows a version of the code where defer is used to close the file after it has been loaded. The load
function calls file.Close()
right before it returns:
func load(fname string) ([]string, error) { ... file, err := os.Open(fname) if err != nil { return nil, err } defer file.Close() ... }
golang.fyi/ch05/anagram2.go
The pattern of opening-defer-closing resources is widely used in Go. By placing the deferred intent immediately after opening or creating a resource allows the code to read naturally and reduces the likeliness of creating a resource leakage.
18.191.144.65