{ Soham Kamani }

About Blog Github Twitter

🌙
☀️

Golang's "defer" Explained (With Examples)

In this post, we will learn about the special defer keyword in Go. We will take an in-depth look into deferred statements and learn their uses, as well as some common pitfalls to look out for.

banner

If you just want to see the code, you can find the complete examples on Github

Basic Usage

The defer keyword instructs a function to execute after the surrounding function completes.

Let’s look at a basic example:

package main

import "fmt"

func main() {
 // When we add `defer` before a function, that function is executed
 // after the surrounding function completes
 defer fmt.Println("this is printed once the function completes")

 fmt.Println("this is printed first")
 fmt.Println("this is printed second")
}

In this case, the “surrounding function” is main

Output:

// this is printed first
// this is printed second
// this is printed once the function completes

Function Scope

It is important to note that defer is scoped to the function inside which it is declared.

Let’s look at an example where we call another function from inside main:

package main

import "fmt"

func main() {
 defer fmt.Println("this is printed once main completes")

 greet()

 fmt.Println("this is printed after greet is called")
}

func greet() {
 // this function executes once `greet()` completes.
 // It is independent of deferred functions declared elsewhere
 defer fmt.Println("printed after the first greeting")

 fmt.Println("first greeting")
}

This gives us the output:

// first greeting
// printed after the first greeting
// this is printed after greet is called
// this is printed once main completes

This is because the deferred function defined in greet is run once greet is completed, whereas the same in main is run after main has completed:

defer is called in greet fist, since its called within main

Execution Sequence (Call Stack)

When there are multiple deferred statements in the same function, they are stored and executed as a stack.

For example, consider a function with multiple defer declarations:

func myFunction() {
 defer fmt.Println("first")
 defer fmt.Println("second")
 defer fmt.Println("third")

 fmt.Println("starting myFunction...")
 // ...
}

When myFunction is called, we will get this output:

starting myFunction...
third
second
first

Each time a function is deferred, it’s pushed onto a stack. Once the function execution completes, the deferred functions are popped, so that they execute in a “first-in-first-out” order:

output is printed according to the stacked order of the deferred statements

Using Defer with Panic

In Go, a function panics if something went wrong. In this case, the function exits immediately, and doesn’t execute any statement after the panic function call.

If any function had been deferred before panic was called, it would be executed before the function exits:

package main

import "fmt"

func main() {
 defer fmt.Println("this is printed once the function panics")

 fmt.Println("this is printed before panic")
 panic("something went wrong")
 fmt.Println("this should not be printed")
} 

This will give the following output:

// this is printed before panic
// this is printed once the function panics
// panic: something went wrong

// goroutine 1 [running]:
// main.main()
//         /Users/soham/src/github.com/sohamkamani/go-defer-example/defer-with-panic.go:11 +0xe4
// exit status 2

In fact, we can even find out if panic was called and prevent the program from exiting using the in-built recover function.

This can only be done within deferred function calls, and provides instructions for what to do in-case panic is called:

package main

import "fmt"

func main() {
 defer func() {
  // we can use the recover function inside the deferred function
  // to tell if it was called after `panic` was called
  if r := recover(); r != nil {
   fmt.Println("recovered from panic: ", r)
  }
 }()

 panic("something went wrong")
 fmt.Println("this should not be printed")
}

Output:

// this is printed before panic
// recovered from panic:  something went wrong

Argument Evaluation

Even though the deferred function is executed when the surrounding function completes, it’s arguments are evaluated immediately:

package main

import "fmt"

// i is a package level variable
var i int

// here, we increment the value of i every time this
// function is called
func count() int {
 i++
 return i
}

func main() {

 // `count` is called here first
 defer fmt.Println("deferred count:", count())

 fmt.Println("current count:", count())
}

This should give you the output:

current count: 2
deferred count: 1

If you want to evaluate everything when the deferred function executes, you should ensure all functions are called within the deferred function call. Let’s see how we can do this for the above example:

package main

import "fmt"

var i int

func count() int {
 i++
 return i
}

func main() {

 defer printCount()

 fmt.Println("current count:", count())
}

// Now, count is evaluated inside `printCount`, so it will
// only be called when the deferred function executes
func printCount() {
 fmt.Println("count:", count())
}

Output:

current count: 1
count: 2

Test your knowledge: What would be the output of this code?

func main() {
 fmt.Println(zeroOrOne())
}

func zeroOrOne() (i int) {
 defer func() {
  i++
 }()

 return i
}

Deferred statements are executed before the functions value is returned.

In this case, the named return value i is incremented once the function completes, but before the value is returned to the calling statement in the main function.


Find this post useful? Subscribe to my newsletter to know when I publish new posts!

When Should You Defer?

Defer is more like a finally block in languages like Java or Python: something that has to be executed after attempting to run some code — whether or not it succeeds.

This is useful in cases where you have to run some clean-up operation after some code executes. For example:

  • Closing a file after opening it.
  • Performing clean-up actions after running a test.

Additionally, you will have to defer any function that needs to handle panics that happen within the function scope.

The main pitfalls you should take note of are argument evaluation and the order of execution for deferred functions, since these can sometimes be confusing for newcomers.

Have you used defer in your application before? What challenges did you face? Let me know in the comments!

You can find the code for all the examples on Github


Like what I write? Join my mailing list, and I'll let you know whenever I write another post. No spam, I promise!

Comments