Friday, June 24, 2011

Control structures - Go defer statement

The defer statement allows you to designate specified functions to be executed just before returning from the current function block. Why would this be useful? In programming we often have to allocate/block/lock resources, but then the program abruptly ends in between and is unable to reach the part of the code where we un-allocate/unblock/unlock these sources, which is not a good thing. By using defer we are ensuring that we free up all these resources whatever happens.

In the example below, we are going to pretend that we are doing some database operations: we make a connection and then attempt some database work. Then we act as if the database operations have crashed and return immediately. This would have left the database connection still dangling, but since we had already called the database disconnect function with a defer, this gets called now, and ensures that the connection is closed.

Full program
package main

import "fmt"

func connectToDB () {
    fmt.Println( "ok, connected to db" )
}

func disconnectFromDB () {
    fmt.Println( "ok, disconnected from db" )
}


func doDBOperations() {
     connectToDB()
     fmt.Println("Defering the database disconnect.")
     defer disconnectFromDB() //function called here with defer
     fmt.Println("Doing some DB operations ...")
     fmt.Println("Oops! some crash or network error ...")
     fmt.Println("Returning from function here!")
     return //terminate the program

     // deferred function executed here just before actually returning, even if there is a return or abnormal termination before
}

func main() {
    doDBOperations()
}

ok, connected to db
Defering the database disconnect.
Doing some DB operations ...
Oops! some crash or network error ...
Returning from function here!
ok, disconnected from db

Multiple defer-ed functions and order of execution

You are allowed to call multiple defer-ed functions. When you do, it behaves like a stack: the functions are execute in Last In First Out (LIFO) order. In the example below the order of deferring is fA() first and then fB(). But the order of execution is reversed or in LIFO order - fB() is executed first, and then fA().

Full program
package main

import "fmt"

func fA() {
    fmt.Println( "this is function A" )
}

func fB() {
    fmt.Println( "this is function B" )
}

func main() {
     defer fA() //defer fA called first
     defer fB() //defer fB called second 

     //program/this function ends here

     //deferred functions executed in LIFO (last in first out) order
}

this is function B
this is function A

5 comments:

  1. thanks a lot Sathish.....tutorials are really useful ....

    ReplyDelete
  2. Why wouldn't you just call disconnectFromDB before return instead of calling it earlier with defer?

    ReplyDelete
    Replies
    1. Imagine if there were multiple places for the doDBOperations() function to exit. You'd have to call disconnectFromDB() at every one of these points, which is redundant and more error prone.
      Using defer, you only have to call disconnectFromDB() once, which is more readable and also ensures you don't miss anything by mistake.

      Delete
  3. Thanks man... nice tutorial :)

    ReplyDelete

If you think others also will find these tutorials useful, kindly "+1" it above and mention the link in your own blogs, responses, and entries on the net so that others also may reach here. Thank you.