1. What will be printed by the following code using defer?
func printVal() {
x := 10
defer fmt.Println(x)
x = 20
}
Output: 10
The critical rule of defer: arguments are evaluated when the defer statement executes, not when the deferred function runs.
The line defer fmt.Println(x) is read while x is still 10. The argument x — the value 10 — is captured at that moment and stored. Later, when the function returns and the deferred call actually runs, it prints the stored 10. The assignment x = 20 after the defer changes the variable, but the deferred call already has its argument.
So the output is 10.
Contrast with a closure:
defer func() { fmt.Println(x) }()
A closure captures the variable, not the value. In that version, the deferred function reads x at return time — it would print 20. The distinction — value captured vs. variable captured — is the whole question.
The interview answer: 10, because deferred arguments are evaluated at defer time.
Answer:
10
The critical rule of defer: arguments are evaluated when the defer statement executes, not when the deferred function runs.
The line defer fmt.Println(x) is read while x is still 10. The argument x — the value 10 — is captured at that moment and stored. Later, when the function returns and the deferred call actually runs, it prints the stored 10. The assignment x = 20 after the defer changes the variable, but the deferred call already has its argument.
So the output is 10.
Contrast with a closure:
defer func() { fmt.Println(x) }()
A closure captures the variable, not the value. In that version, the deferred function reads x at return time — it would print 20. The distinction — value captured vs. variable captured — is the whole question.
The interview answer: 10, because deferred arguments are evaluated at defer time.
2. What is the execution order of multiple deferred functions inside the same function scope?
Answer: LIFO — Last In, First Out. The last deferred call runs first.
Each defer statement pushes its function onto a per-function stack. When the surrounding function returns, the runtime pops that stack, executing deferred calls in reverse order — the most recently deferred runs first.
This is deliberate, and it supports the classic cleanup pattern:
func copyFile() {
f, _ := os.Open(src)
defer f.Close()
g, _ := os.Create(dst)
defer g.Close()
// ...
}
Resources acquired later are released first — g closes before f — which is exactly the right order for dependent resources (you close the destination before the source, matching acquisition in reverse). The LIFO ordering is guaranteed, not incidental.
The interview answer: multiple defers run in LIFO order, like a stack being popped.
Answer:
LIFO — Last In, First Out. The last deferred call runs first.
Each defer statement pushes its function onto a per-function stack. When the surrounding function returns, the runtime pops that stack, executing deferred calls in reverse order — the most recently deferred runs first.
This is deliberate, and it supports the classic cleanup pattern:
func copyFile() {
f, _ := os.Open(src)
defer f.Close()
g, _ := os.Create(dst)
defer g.Close()
// ...
}
Resources acquired later are released first — g closes before f — which is exactly the right order for dependent resources (you close the destination before the source, matching acquisition in reverse). The LIFO ordering is guaranteed, not incidental.
The interview answer: multiple defers run in LIFO order, like a stack being popped.
3. What happens when a panic occurs and is NOT handled by recover()?
Answer: The panic unwinds the goroutine’s stack, runs all deferred functions, prints the panic message with a stack trace, and terminates the program.
A panic is Go’s mechanism for “something unrecoverably wrong happened at runtime.” The sequence when it’s not recovered:
- The current function stops executing and starts unwinding — control returns up the call stack.
- Each deferred function along the way still runs (defers execute during unwinding). This is why
defercleanup is reliable even under panics. - If no
recover()intercepts it, the runtime prints the panic value and a stack trace, then exits the process with a non-zero status.
Key points: the panic is not converted into an error value returned to main — it kills the process. And the unwinding is per-goroutine: a panic in one goroutine that’s not recovered crashes the whole program (unless that goroutine recovers), because there’s nowhere else to go.
The interview answer: unhandled panics run defers, print the panic and stack trace, and terminate execution.
Answer:
The panic unwinds the goroutine’s stack, runs all deferred functions, prints the panic message with a stack trace, and terminates the program.
A panic is Go’s mechanism for “something unrecoverably wrong happened at runtime.” The sequence when it’s not recovered:
- The current function stops executing and starts unwinding — control returns up the call stack.
- Each deferred function along the way still runs (defers execute during unwinding). This is why
defercleanup is reliable even under panics. - If no
recover()intercepts it, the runtime prints the panic value and a stack trace, then exits the process with a non-zero status.
Key points: the panic is not converted into an error value returned to main — it kills the process. And the unwinding is per-goroutine: a panic in one goroutine that’s not recovered crashes the whole program (unless that goroutine recovers), because there’s nowhere else to go.
The interview answer: unhandled panics run defers, print the panic and stack trace, and terminate execution.
4. Where can recover() be called to successfully intercept a panic?
Answer: Exclusively inside a deferred function.
recover() only works when called directly from a deferred function. The reason is timing: a panic stops normal execution, so the only code that runs afterward is deferred code. A direct call to recover() during normal execution (not from a defer) always returns nil — there’s no panic in flight at that moment.
The canonical pattern:
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
panic("boom") // intercepted by the deferred recover
}
Two constraints make this subtle:
recover()must be called within the deferred function (directly, not from a helper it calls).- It only catches a panic happening in the same goroutine.
The interview answer: recover() works only inside a deferred function.
Answer:
Exclusively inside a deferred function.
recover() only works when called directly from a deferred function. The reason is timing: a panic stops normal execution, so the only code that runs afterward is deferred code. A direct call to recover() during normal execution (not from a defer) always returns nil — there’s no panic in flight at that moment.
The canonical pattern:
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
panic("boom") // intercepted by the deferred recover
}
Two constraints make this subtle:
recover()must be called within the deferred function (directly, not from a helper it calls).- It only catches a panic happening in the same goroutine.
The interview answer: recover() works only inside a deferred function.
Premium Content
Unlock defer, panic & recover and all premium lessons with a subscription.
From ₹199.99/year — See plans