<-home
defer in Go Doesn’t Always Reflect the Latest Value
Table of Contents
The Problem
Consider the following Go function:
func f() error {
var status string // Initialized as an empty string
defer notify(status)
if err := foo(); err != nil {
status = "error" // Status changes here, but defer does not reflect this change
return err
}
status = "success"
return nil
}
We might expect notify(status) to reflect the latest value of status.
However, in reality, it always receives an empty string (""), regardless of how status is updated later.
The Reason why status is not updated
var status string // ""
statusis initialized as an empty stringdeferevaluates its arguments immediately when declared.- When
defer notify(status)is called,statusis still an empty string. - Thus,
defercaptures the empty string("") at this moment and never updates, even ifstatuschanges later.
- When
- Even though
statusis modified in the function, the deferred function call was already bound to the original value ofstatus(empty string). - In Go, when a function is deferred, its arguments are evaluated and stored immediately—not when the function actually executes.
How to Fix It?
To ensure that notify(status) receives the latest value of status, we can use two different approaches.
1. Using a Closure (Anonymous Function)
defer func() { notify(status) }()
- Instead of immediately passing
statustonotify, we defer an anonymous function. - This function captures
statusat the momentdeferexecutes, ensuring it reflects the latest value. - When
notify(status)is finally called, it uses the updated value ofstatus.
2. Passing a Pointer
defer notify(&status)
- Instead of passing the value of
status, we pass its memory address (&status). - Since
defercaptures the pointer, any changes tostatusare reflected at execution time. - However, this requires modifying
notifyto accept a pointer parameter.
// If using a pointer, notify must be updated:
// Dereference the pointer to get the latest value
func notify(status *string) {
fmt.Println(*status)
}
3. Comparison of Solutions
| Approach | How it Works | Requires Function Signature Change? | Best Use Case |
|---|---|---|---|
| Closure (Anonymous Function) | Captures status at execution time |
No | Works for most cases |
| Pointer Passing (&status) | Passes a pointer, reflects latest value | Yes | When working with functions that support pointers |
Conclusion
- Deferred functions capture arguments immediately, not when they actually run.
- If the deferred function needs the latest value, use:
- Closures (Anonymous Functions): Recommended, as they don’t require modifying function signatures.
- Pointers (&status): Useful when modifying function behavior explicitly.