Beginner · Control flow and functions · topic 10 of 46 · 30 min
defer: clean up at the end
Explanation
The problem defer solves
When you open a file or a database connection, you must close it when you are done, on every path out of the function: the normal end, every early return and every error. Writing Close() before each return is easy to forget. defer puts the clean-up right next to the setup.
func readConfig(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close() // runs when readConfig returns, on any path
return io.ReadAll(f)
}Rule 1: deferred calls run last-in, first-out
func main() {
defer fmt.Println("1st deferred")
defer fmt.Println("2nd deferred")
fmt.Println("main body")
}
// main body
// 2nd deferred
// 1st deferredRule 2: arguments are evaluated immediately
x := 1
defer fmt.Println("deferred x =", x)
x = 2
fmt.Println("now x =", x)
// now x = 2
// deferred x = 1x := 1
defer func() { fmt.Println("closure sees", x) }()
x = 2
// closure sees 2Rule 3: deferred functions can change named results
func process() (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("process failed: %w", err)
}
}()
return errors.New("disk full")
}
// process() returns "process failed: disk full"Common uses
| Setup | Deferred clean-up |
|---|---|
| f, err := os.Open(p) | defer f.Close() |
| mu.Lock() | defer mu.Unlock() |
| resp, err := http.Get(u) | defer resp.Body.Close() |
| rows, err := db.Query(...) | defer rows.Close() |
| ctx, cancel := context.WithTimeout(...) | defer cancel() |
| start := time.Now() | defer func() { log.Println(time.Since(start)) }() |
Mistakes to avoid
- Deferring before checking the error: f, err := os.Open(p); defer f.Close(); if err != nil ... If Open failed, f is nil and Close may panic. Check err first, then defer.
- defer inside a long loop: deferred calls only run when the FUNCTION returns, not at the end of each loop round. Opening 10,000 files in a loop with defer f.Close() keeps all of them open until the end. Move the loop body into its own function.
- Expecting defer to run after os.Exit: os.Exit ends the program immediately and skips all deferred calls. log.Fatal calls os.Exit too.
for _, path := range paths {
if err := processFile(path); err != nil { // each file opened AND closed
return err
}
}
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// ... work with f ...
return nil
}Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
defer: clean up at the end · 1/7
defer is Go's tool for clean-up. It schedules a call to run when the function returns.
Prefer a video by a teacher? Find free videos on “golang defer explained order arguments evaluated” on YouTube ↗
In real life: Timing a payment API call
A payment service logs how long each call to the bank takes, so slow banks can be spotted. One deferred line at the top of the function records the duration on every path, success or error, without repeating the timing code before each return.
package main
import (
"fmt"
"time"
)
func callBank(ref string) error {
start := time.Now()
defer func() {
fmt.Println(ref, "took", time.Since(start).Round(time.Millisecond))
}()
time.Sleep(120 * time.Millisecond) // pretend to call the bank
return nil
}
func main() {
callBank("UPI-4821")
}Practice (0/4 done)
What does this print? for i := 0; i < 3; i++ { defer fmt.Print(i, " ") }
What does f() return? func f() (n int) { defer func() { n *= 2 }() return 5 }
Which line is the bug? 1: f, err := os.Open(path) 2: defer f.Close() 3: if err != nil { 4: return err 5: }
Write a function that prints "start", defers printing "cleanup 1" and "cleanup 2", then prints "working". Predict the output before running it, then run it to check.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Loops: for is the only loopControl flow and functions · same module
- panic and recover: only for the truly unexpectedPointers, interfaces and errors
- Go language interview questionsCareers and interviews
- LINQ in depth: grouping, joins, deferred execution, IQueryableC# and .NET · Collections, generics and LINQ
- try-with-resources and clean-upJava Programming (Core to Spring Boot) · Exceptions and generics
- context: timeouts and cancellationConcurrency: goroutines, channels and context
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
When does a deferred call run?
Answer: When the surrounding function returns
Deferred calls run as the function returns, including during a panic.
x := 1; defer fmt.Println(x); x = 5. What is printed at the end?
Answer: 1
Arguments are evaluated when the defer statement runs, so 1 is printed.
Do deferred calls run after os.Exit(1)?
Answer: No, os.Exit ends the program immediately
os.Exit skips deferred calls; so does log.Fatal, which calls os.Exit.
What is the usual way to make sure a mutex is unlocked?
Answer: mu.Lock(); defer mu.Unlock()
Lock, then immediately defer Unlock, so every return path unlocks.
What runs when a function panics?Related
Answer: Its deferred calls, then the callers' deferred calls
The panic unwinds the stack, running deferred calls as it goes.
Before Go 1.22, what did for i := 0; i < 3; i++ { defer func() { fmt.Print(i) }() } print?Related
Answer: 333
All closures shared one i, which was 3 when they ran. Since Go 1.22 it prints 210.
What is deferred execution?Related
Answer: A query runs only when its results are enumerated
The query is a recipe until enumerated.
From LINQ in depth: grouping, joins, deferred execution, IQueryable
How does a Go function usually report failure?Related
Answer: By returning an error value as the last result
Go functions return an error as the last result; nil means success.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Control flow and functions”. The module exam comes next.
- Module 2 exam: Control flow and functions · 13 questions · 20 min, without help. 5 topics of this module still to finish first.
- Then the next module, Collections and structs. 46 topics left in the course before the final exam.
← Previous: Variadic functions, function values and closures