Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
181: Common Go Interview Questions on Defer Semantics
If you've been looking at Go interview prep, you'll notice that defer is a favorite topic. On the surface, it seems simple: "run this at the end of the function." But interviewers love it because there are three specific "gotchas" that trip up almost everyone who hasn't spent a few years in a production Go codebase. Let's explore these by actually breaking some code.
The Stack Effect
I remember the first time I used multiple defer statements; I assumed they ran in the order they were written. Let's test that theory. I'll write a quick function that pushes a few strings to a slice via deferred calls.
package main
import "fmt"
func main() {
fmt.Println("Starting execution...")
defer fmt.Println("First defer")
defer fmt.Println("Second defer")
defer fmt.Println("Third defer")
fmt.Println("Ending execution...")
}
When I run this, I expect "First, Second, Third." But the output is actually:
Starting execution...
Ending execution...
Third defer
Second defer
First defer
Right. It's a stack (LIFO). Go pushes every defer call onto a stack and pops them off when the function returns. This is crucial when you're dealing with resources—like opening a file and then locking a mutex. You have to unlock the mutex before you close the file, so you lock first, then defer the unlock. The last thing you "set up" is the first thing that gets "torn down."
The Argument Evaluation Trap
Now, here is where it gets tricky. Let's see what happens when we pass a variable to a deferred function. I'll create a variable, defer a print of that variable, and then change the variable's value before the function exits.
func argumentTest() {
i := 0
defer fmt.Println("Deferred value:", i)
i++
fmt.Println("Final value:", i)
}
I'm betting the output will be Final value: 1, Deferred value: 1. Let's run it. Nope. It prints:
Final value: 1
Deferred value: 0
Why? Because Go evaluates the arguments to the deferred function immediately, not when the function actually executes. When I wrote defer fmt.Println("...", i), Go took the current value of i (which was 0) and saved it. The fact that I incremented i later didn't matter; the 0 was already "baked in."
If I actually wanted the final value of i, I'd have to wrap the logic in a closure (an anonymous function) so that the variable is accessed at the time of execution, not at the time of definition:
defer func() {
fmt.Println("Deferred value:", i)
}()
Manipulating the Return Value
This is the "final boss" of defer questions. Can a deferred function change the value a function returns? Let's try it with a standard return first.
func tryChange() int {
i := 0
defer func() {
i++
}()
return i
}
I'll run this, and it returns 0. I'm not surprised. The return i statement effectively does two things: it sets the return value to 0, and then it triggers the deferred functions. By the time i++ happens, the return value has already been decided.
But wait—Go has named return parameters. Let's see if that changes the game.
func tryChangeNamed() (result int) {
defer func() {
result++
}()
return 0
}
Now, when I run this, it returns 1. This is a powerful (and sometimes dangerous) pattern. Because the return variable is named result, it's essentially a local variable that persists throughout the function. The deferred closure captures a reference to result and modifies it after the return 0 statement has executed, but before the caller actually receives the value.
You'll see this a lot in production code for error handling. If a function returns (int, error), you can use a deferred closure to check if the error is nil and, if not, wrap that error with more context before it heads back up the call stack.
📋 Practical Task
Exercise: Fixing the Deferred Return Value Bug
You are reviewing a teammate's code for a custom rounding utility. The goal is to return a value, but if that value is negative, a defer block should "intercept" it and flip it to a positive number (absolute value). However, the current implementation is failing to modify the return value.
Current Broken Code:
package main
import "fmt"
func AbsoluteValue(n int) int {
defer func() {
if n < 0 {
n = -n
}
}()
return n
}
func main() {
fmt.Println(AbsoluteValue(-50)) // Currently prints -50, should print 50
}
Your Task: Modify the AbsoluteValue function signature and the return statement so that the deferred closure successfully modifies the returned integer. Ensure the output of main() becomes 50.
There are no comments for now.