Defer Keyword in Golang

In Go, the defer keyword is used to schedule a function call to be executed just before the surrounding function returns. It’s a way to ensure that certain cleanup or finalization tasks are performed regardless of how the function exits, whether it’s due to a return statement, panic, or successful completion.

Here are some key points about the defer keyword in Go:

  • The defer statement is followed by a function call. This function call is deferred until the surrounding function’s execution is complete.
  • Deferred function calls are executed in reverse order, meaning the last deferred function call will be executed first, and the first one will be executed last.
  • defer statements are often used for tasks such as closing files, releasing resources, unlocking mutexes, or logging.

Example of using the defer keyword:

package main

import "fmt"

func main() {
    defer fmt.Println("This will be printed at the end.")
    fmt.Println("This will be printed first.")
}

Output:

This will be printed first.
This will be printed at the end.

In this example, the defer statement is used to defer the execution of fmt.Println("This will be printed at the end."). As a result, this line will be executed after the surrounding function (main) completes, even though it appears before the other fmt.Println statement.

Another common use case for defer is resource management:

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Create("example.txt")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer file.Close() // This will be executed when main exits
	fmt.Println("File created successfully.")
	// ...
}

In this example, the defer statement ensures that the Close method of the file is called when the main function exits, regardless of how it exits. This is a safe and clean way to ensure that resources are properly managed.

The defer keyword is a powerful tool for managing the execution flow of your code and ensuring important actions are taken before a function exits. It’s particularly useful for resource cleanup and finalization tasks.

Here’s another example that demonstrates how defer can be used to time the execution of a function:

package main

import (
	"fmt"
	"time"
)

func main() {
	defer trackTime("main")()

	fmt.Println("Doing some work...")
	time.Sleep(2 * time.Second)
}

func trackTime(taskName string) func() {
	start := time.Now()
	fmt.Printf("Starting %s...\n", taskName)

	return func() {
		elapsed := time.Since(start)
		fmt.Printf("%s took %s\n", taskName, elapsed)
	}
}

Output:

Starting main...
Doing some work...
main took 2.001066832s

In this example, we define a function trackTime that takes a task name and returns a closure (a function) that calculates and prints the time elapsed since the function call. The trackTime function is designed to be deferred in the main function.

Inside the main function, we defer the call to trackTime("main")(). This syntax first calls trackTime("main") to get the closure, and then immediately calls the returned closure using ().

This pattern allows us to automatically measure and print the time taken by the main function’s execution. The deferred trackTime function is executed after the main function completes, providing insights into the duration of the main function’s execution.

Using defer in combination with closures provides a clean way to encapsulate tasks and ensure they are executed at the appropriate time.