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.