defer
Many resources require cleanup:
-
Files
-
Network connections
-
Database connections
-
Mutexes
Without careful cleanup, resources may leak.
Example:
file, err := os.Open("data.txt")
if err != nil {
return err
}
// Use file
file.Close()If the function returns early before file.Close(), the file remains open.
Using defer
file, err := os.Open("data.txt")
if err != nil {
return err
}
defer file.Close()defer schedules a function call to execute when the current function returns.
Cleanup is guaranteed regardless of how the function exits.
Basic Example
func main() {
fmt.Println("A")
defer fmt.Println("B")
fmt.Println("C")
}Output:
A
C
BDeferred functions execute after the surrounding function finishes.
Multiple defer Statements
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}Output:
3
2
1Deferred calls execute in Last In, First Out (LIFO) order.
Think of them as a stack.
Typical Uses
Closing files:
file, err := os.Open(...)
if err != nil {
return err
}
defer file.Close()Unlocking mutexes:
mu.Lock()
defer mu.Unlock()Closing network connections:
conn, err := net.Dial(...)
if err != nil {
return err
}
defer conn.Close()Always Check Errors First
Correct:
file, err := os.Open(...)
if err != nil {
return err
}
defer file.Close()Incorrect:
file, err := os.Open(...)
defer file.Close()
if err != nil {
return err
}Only defer cleanup after confirming the resource was successfully acquired.
defer Captures Function Arguments Immediately
Example:
func main() {
x := 10
defer fmt.Println(x)
x = 20
}Output:
10Reason:
The argument x is evaluated when the defer statement executes.
The function call itself is delayed, but its arguments are already fixed.
Anonymous Functions Behave Differently
func main() {
x := 10
defer func() {
fmt.Println(x)
}()
x = 20
}Output:
20Why?
The anonymous function executes later and reads the variable when it runs.
It does not capture the value at the moment of defer.
Comparison
Argument captured immediately:
defer fmt.Println(x)Reads variable later:
defer func() {
fmt.Println(x)
}()Mental model:
defer f(x)
│
└── arguments evaluated immediately
defer func(){ ... }
│
└── function body executes laterdefer Inside Loops
Example:
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
}Output:
2
1
0Each iteration evaluates i immediately.
Deferred calls execute later in reverse order.
defer Scope
Unlike C++ destructors, defer is function-scoped, not block-scoped.
Example:
func main() {
{
defer fmt.Println("Inner")
}
fmt.Println("Body")
}Output:
Body
InnerThe deferred call waits until the function returns, not until the block ends.
Common Beginner Mistakes
Forgetting defer
Writing cleanup only at the end of the function can leak resources if the function returns early.
Deferring Before Checking Errors
Only defer cleanup after successful resource acquisition.
Confusing Deferred Arguments and Deferred Closures
These are different:
defer fmt.Println(x)prints the value at the time of defer.
defer func() {
fmt.Println(x)
}()prints the value when the deferred function actually executes.
Comparison with C++
| Go | C++ |
|---|---|
defer file.Close() | RAII cleanup |
| Cleanup at function exit | Cleanup at scope exit |
| LIFO deferred calls | Reverse destruction order |
| Explicit cleanup scheduling | Automatic destructor invocation |
Both aim to guarantee cleanup, but they use different language mechanisms.
Session Summary
Topics covered:
-
defer -
Resource cleanup
-
Execution order
-
LIFO behavior
-
Deferred arguments
-
Deferred anonymous functions
-
Function scope vs block scope
Practical skills gained:
-
Scheduling cleanup immediately after acquiring a resource
-
Predicting deferred execution order
-
Understanding when values are captured
-
Distinguishing deferred function calls from deferred closures
Key Takeaways
-
deferschedules a function to execute when the surrounding function returns. -
Deferred calls execute in reverse order (LIFO).
-
Always place
deferimmediately after successful resource acquisition. -
defer f(x)evaluates arguments immediately. -
defer func(){...}()evaluates the function body later. -
deferis function-scoped, not block-scoped.
Next Topics
- File I/O
- Reading and writing files
- JSON encoding and decoding
- Working with the standard library
- Go Learning - Session 9