Golang Tutorial

Fundamentals

Control Statements

Functions & Methods

Structure

Arrays & Slices

String

Pointers

Interfaces

Concurrency

Defer in Golang

The defer statement in Go is a powerful feature used to ensure that a function call is performed later in a program's execution, usually for cleanup purposes. It's typically used with resource release operations like closing files, disconnecting network connections, or unlocking resources.

Let's explore the defer statement in detail.

1. Basic Usage of Defer

The basic idea is that defer postpones the execution of a function until the surrounding function returns.

package main

import "fmt"

func main() {
    defer fmt.Println("World")
    fmt.Println("Hello")
}

Output:

Hello
World

The above program will print "Hello" first, and then "World" because the call to fmt.Println("World") is deferred.

2. Stack of Defers

If multiple defer statements are used, they will be executed in a LIFO (Last-In, First-Out) order:

package main

import "fmt"

func main() {
    defer fmt.Println("First defer")
    defer fmt.Println("Second defer")
    fmt.Println("Regular statement")
}

Output:

Regular statement
Second defer
First defer

3. Using Defer with Resources

A common use case for defer is to release resources, like closing files:

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Create("sample.txt")
	if err != nil {
		panic(err)
	}

	defer file.Close()

	file.WriteString("This is a sample text.")
}

Here, the file.Close() method will be called after all other operations in the main function are completed, ensuring that the file is properly closed before the program exits.

4. Defer in Loops

Be cautious when using defer inside loops, as it can cause resource exhaustion since the deferred calls won't execute until the surrounding function exits.

For example, if you're opening files in a loop and deferring their closures, you might run into a situation where you've opened too many files and can't open any more until the surrounding function exits and the deferred closures execute.

5. Deferred Function Arguments

The arguments of a deferred function are evaluated when the defer statement is executed, not when the deferred function is called:

package main

import "fmt"

func main() {
    i := 1
    defer fmt.Println(i)  // This will print "1"
    i++
    fmt.Println("Done")
}

Output:

Done
1

Even though the value of i was changed after the defer statement, the deferred function prints the value of i at the time the defer statement was executed.

Conclusion

The defer statement in Go is a handy tool for ensuring certain operations are performed after the main logic of a function. Whether you're cleaning up resources or ensuring certain post-conditions, defer can help make your code cleaner and more maintainable.

  1. How to Use Defer in Golang:

    • defer is used to ensure that a function call is performed later in a program's execution, usually for cleanup operations.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer fmt.Println("World")
          fmt.Println("Hello")
      }
      
  2. Deferred Function Calls in Golang:

    • Deferred functions are executed after the surrounding function returns but before the actual return.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer fmt.Println("Second")
          fmt.Println("First")
      }
      
  3. Order of Execution with Defer in Golang:

    • Deferred functions are executed in Last In, First Out (LIFO) order.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer fmt.Println("First")
          defer fmt.Println("Second")
          fmt.Println("Last")
      }
      
  4. Common Use Cases for Defer in Golang:

    • Closing files, releasing resources, unlocking mutexes, and logging are common use cases for defer.
    • Example:
      package main
      
      import (
          "fmt"
          "os"
      )
      
      func main() {
          file, err := os.Open("example.txt")
          if err != nil {
              fmt.Println("Error opening file:", err)
              return
          }
          defer file.Close()
      
          // File processing code...
      }
      
  5. Error Handling with Defer in Golang:

    • Defer can be used to handle errors by deferring the execution of cleanup operations.
    • Example:
      package main
      
      import (
          "fmt"
          "os"
      )
      
      func main() {
          file, err := os.Open("example.txt")
          if err != nil {
              fmt.Println("Error opening file:", err)
              return
          }
          defer func() {
              if err := file.Close(); err != nil {
                  fmt.Println("Error closing file:", err)
              }
          }()
      
          // File processing code...
      }
      
  6. Panics and Recover with Defer in Golang:

    • panic triggers a runtime error, and recover is used to capture and handle panics, often in deferred functions.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer func() {
              if r := recover(); r != nil {
                  fmt.Println("Recovered from panic:", r)
              }
          }()
      
          // Trigger a panic
          panic("Panic occurred!")
      }
      
  7. Benefits of Using Defer in Golang:

    • defer simplifies cleanup and resource management, making code more readable.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer cleanup()
          // Code...
      }
      
      func cleanup() {
          // Cleanup operations...
          fmt.Println("Cleanup completed.")
      }
      
  8. Defer vs Immediate Execution in Golang:

    • Defer delays the execution until the surrounding function returns, while immediate execution happens immediately.
    • Example:
      package main
      
      import "fmt"
      
      func main() {
          defer fmt.Println("Deferred execution")
          fmt.Println("Immediate execution")
      }
      
  9. Practical Examples of Defer in Golang:

    • File operations, mutex unlocks, and resource cleanup are practical examples of defer.
    • Example:
      package main
      
      import (
          "fmt"
          "sync"
      )
      
      func main() {
          var wg sync.WaitGroup
          wg.Add(1)
      
          go func() {
              defer wg.Done()
              fmt.Println("Goroutine completed.")
          }()
      
          wg.Wait()
          fmt.Println("Main function completed.")
      }