How to Use Deferred Function Calls in Golang

Created
Modified

Deferred Function Calls

A defer statement is an ordinary function or method call prefixed by the keyword defer. The function and argument expressions are evaluated when the statement is executed, but the actual call is deferred until the function that contains the defer statement has finished, whether normally, by executing a return statement or falling off the end, or abnormally, by panicking. Any number of calls may be deferred;they are executed in the reverse of the order in which they were deferred.

package main

import "fmt"

func main() {
  
  defer fmt.Println("World") // deferred until main() exits

  // Multiple defer statements
  defer fmt.Println("B")
  defer fmt.Println("A")

  fmt.Println("Hello")

}
$ go run main.go
Hello
A
B
World

Examples

A defer statement is often used with paired operations like open and close, connect and dis- connect, or lock and unlock to ensure that resources are released in all cases, no matter how complex the control flow. The right place for a defer statement that releases a resource is immediately after the resource has been successfully acquired.

package main

import (
  "log"
  "net/http"
)

func main() {

  resp, err := http.Get("https://installmd.com/")
  if err != nil {
    log.Println(err)
  }
  defer resp.Body.Close()

}
$ go run main.go

Risky

Because deferred functions aren’t executed until the very end of a function’s execution, a defer statement in a loop deserves extra scrutiny. The code below could run out of file descriptors since no file will be closed until all files have been processed:

for _, filename := range filenames {
  f, err := os.Open(filename)
  if err != nil {
    return err
  }
  defer f.Close() // NOTE: risky; could run out of file descriptors
  // ...process f...
}

One solution is to move the loop body, including the defer statement, into another function that is called on each iteration.

for _, filename := range filenames {
  f, err := os.Open(filename)
  if err != nil {
    return err
  }
  defer f.Close() // NOTE: risky; could run out of file descriptors
  // ...process f...
}

Related Tags