Deferring function calls

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.

Using defer

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:

  • Closing open files
  • Releasing network resources
  • Closing the Go channel
  • Committing database transactions
  • And do on

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.144.65