defer was introduced in Go to simplify resource management (e.g., files, mutexes, connections) — for any case where it is necessary to ensure that operations are executed at the very end of the function's execution. Historically, similar constructs existed in other languages (finally in Java, try-with-resources) but Go implements a more explicit and understandable pattern.
Problem: You always need to be sure that resources are released, even if an error occurs or a panic happens. Double resource closure or leaks are a common problem in classical programming style.
Solution: Everything declared with defer in a function or method is pushed onto a call stack and will be executed in reverse order before exiting the function. This guarantees resource release even in the face of exceptions (panic) or premature returns.
Example code:
func processFile() error { f, err := os.Open("filename.txt") if err != nil { return err } defer f.Close() // closing the file will happen at the end // work with the file return nil }
Key features:
Will defers execute if a panic occurs within the function?
Yes! All defer functions will be called even in the case of panic, this is the main mechanism of "finalization".
When are the function arguments passed to defer evaluated?
At the time of the defer declaration, not when it is actually executed. Therefore, if using variables that change later, this should be taken into account:
a := 1 defer fmt.Println(a) a = 2 // will output 1, not 2
How does defer work inside a loop? Will this lead to memory leaks?
If defer is used in each iteration of the loop, all defers will only execute after the entire function completes, not after each iteration — the entire stack of defer functions will accumulate, which can lead to excessive memory consumption.
for i := 0; i < 3; i++ { defer fmt.Println(i) }
Opening a thousand files in a loop, and using defer for each one. All files will only be closed at the end of the entire function, and resources will be held up, leading to a "leak" — exceeding the limit of open files.
Pros:
Cons:
Using local functions in a loop, where defer is applied only for the scope of that file, not for the entire handler:
for _, name := range fileNames { func() { f, _ := os.Open(name) defer f.Close() // work with f }() }
Pros:
Cons: