Closing a file right after opening it, before you've even used it yet, looks backwards the first time you see it in Go — until you realize defer schedules that cleanup for later, letting you write it right where the resource was acquired.
3 min read
func readConfig() error {
file, err := os.Open("config.txt")
if err != nil {
return err
}
defer file.Close() // scheduled now, runs when readConfig() returns
// ... read and use file ...
return nil
}defer schedules a function call to run right before the enclosing
function returns — regardless of how it returns, whether that's a normal
return, an early return from an error check, or even a panic. Writing
defer file.Close() immediately after a successful os.Open() means the
cleanup is guaranteed to happen, and — just as importantly — it's written
right next to the code that acquired the resource, instead of duplicated at
every single point the function might return from.
Without defer, a function with multiple possible return points (several
early error checks, for instance) has to remember to call Close() before
every single one of them — miss one, and you've leaked a file handle
exactly the way the file-handling lesson elsewhere in this curriculum
warns about. defer removes that entire category of mistake: write it once,
right after acquiring the resource, and every possible exit path is covered
automatically.
func example() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}
// Prints: 3, 2, 1Deferred calls stack up and unwind in last-in-first-out order — the same order you'd want if you opened resource A, then B, then C: closing should happen C, then B, then A, unwinding in the reverse order they were acquired, exactly matching how nested resource acquisition naturally needs to be released.
mu.Lock(); defer mu.Unlock() — guaranteeing the unlock happens no matter how the function
exits, a real safety net against forgetting to unlock on an early return.defer combined with Go's recover()
function is the standard mechanism for catching a panic and preventing it
from crashing the whole program, covered in more depth alongside Go's
error-handling philosophy.defer is one of the most immediately useful, idiomatic pieces of Go —
reaching for it by habit right after any resource acquisition (a file, a
lock, a network connection) is what makes resource leaks a rare bug in
well-written Go rather than a constant background risk.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. When does a deferred function call actually run?
2. Why is writing `defer file.Close()` right after a successful `os.Open()` considered good practice?
3. In what order do multiple deferred calls in the same function run?
Go