Go > Core Go Basics > Functions > Defer statements
Understanding Defer Statements in Go
This example demonstrates the usage of the defer
statement in Go, which schedules a function call to be executed after the surrounding function returns. We'll explore how defer
works with multiple statements and how it interacts with function arguments.
Basic Defer Example
This code snippet shows the most basic use of defer
. The fmt.Println("World!")
statement is deferred until after fmt.Println("Hello,")
has finished executing. Therefore, the output will be 'Hello,' followed by 'World!'. The defer
keyword ensures the function call happens at the end of the enclosing function, regardless of how the function exits (normal return or panic).
package main
import "fmt"
func main() {
defer fmt.Println("World!")
fmt.Println("Hello,")
}
Defer Stack (LIFO)
Go executes deferred functions in Last-In-First-Out (LIFO) order. This means the last defer
statement you write will be the first one executed when the surrounding function returns. In this example, the output will be 'Start', then 'One', then 'Two', then 'Three'.
package main
import "fmt"
func main() {
defer fmt.Println("Three")
defer fmt.Println("Two")
defer fmt.Println("One")
fmt.Println("Start")
}
Defer and Function Arguments
It's important to understand how defer
interacts with function arguments. The arguments to the deferred function are evaluated at the time the defer
statement is executed, not when the deferred function is actually called. In this example, the value of i
is 1 when the defer
statement is encountered. The `increment` function increases the value to 2. Therefore, the output will be 'Current value: 2' followed by 'Deferred value: 1'. The deferred function uses the value of `i` at the time it was deferred.
package main
import "fmt"
func increment(i int) int {
i++
return i
}
func main() {
i := 1
defer fmt.Println("Deferred value:", i)
i = increment(i)
fmt.Println("Current value:", i)
}
Real-Life Use Case: Closing Resources
A common use case for defer
is to ensure that resources like files are closed properly, even if errors occur. In this example, file.Close()
is deferred. This guarantees that the file will be closed when main()
returns, regardless of whether an error occurred while opening or reading the file. This is crucial for preventing resource leaks.
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()
// Read from the file (implementation omitted for brevity)
fmt.Println("File opened successfully.")
}
Best Practices
defer
for cleanup tasks like closing files, unlocking mutexes, and closing network connections.
Interview Tip
Be prepared to explain how defer
works, especially the LIFO ordering and the timing of argument evaluation. You might be asked to predict the output of code snippets involving defer
.
When to Use Them
Use defer
when you need to guarantee that a function will be executed regardless of how the enclosing function returns. This is especially important for resource management.
Alternatives
While defer
is very convenient, you could manually place the cleanup code at the end of a function and in all error return paths. However, this is much more error-prone and less readable than using defer
.
Pros
Cons
FAQ
-
What happens if a deferred function panics?
If a deferred function panics, the panic will be caught by the Go runtime, and the remaining deferred functions will still be executed before the program crashes (if the panic isn't recovered). -
Can I use
defer
inside a loop?
Yes, you can usedefer
inside a loop. However, be aware that the deferred functions will accumulate and be executed when the loop's surrounding function returns. This might lead to unexpected behavior or resource exhaustion if not handled carefully.