Variable Shadowing: The := Trap
One of the most common pitfalls in Go is variable shadowing with the short declaration operator :=.
The := operator creates a new variable in the current scope. If a variable with the same name exists in an outer scope, you’ve just shadowed it!
The Problem
x := 10
if true {
x := 20 // New variable 'x' (Shadowing). Outer 'x' is still 10.
fmt.Println(x) // Prints: 20
}
fmt.Println(x) // Prints: 10 (outer x unchanged!)
The Solution
If you want to modify the existing variable, use the assignment operator = instead:
x := 10
if true {
x = 20 // Assigns to outer 'x'
fmt.Println(x) // Prints: 20
}
fmt.Println(x) // Prints: 20
Always check your scopes! When in doubt, use explicit = assignment instead of := inside nested blocks.
Goroutine Pitfalls
The Mystery of Empty Output
Why did my code print nothing?When main() finishes, the program dies instantly. It does NOT wait for background goroutines to finish.
The Problem
func main() {
go func() {
fmt.Println("Hello from goroutine")
}()
// main() exits immediately, goroutine gets killed!
}
The Solution
Use WaitGroups or Channels to make main wait:
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Hello from goroutine")
}()
wg.Wait() // Wait for goroutine to finish
}
Channel Deadlocks
The Deadlock Trap
Fatal Error: All goroutines are asleep!Writing to an unbuffered channel blocks forever until someone reads it. If you write in main without a background reader, main blocks forever → Deadlock.
The Problem
func main() {
messageChain := make(chan string)
messageChain <- "Hello" // BLOCKS FOREVER - no reader!
msg := <-messageChain // Never reached
fmt.Println(msg)
}
The Solution
Either use a buffered channel or send/receive in separate goroutines:
Option 1: Buffered Channel
func main() {
messageChain := make(chan string, 1) // Buffer size 1
messageChain <- "Hello" // Doesn't block
msg := <-messageChain
fmt.Println(msg)
}
Option 2: Separate Goroutine
func main() {
messageChain := make(chan string)
go func() {
messageChain <- "Hello" // Send in background
}()
msg := <-messageChain // Receive in main
fmt.Println(msg)
}
Unbuffered channels provide synchronization - the sender blocks until a receiver is ready. Buffered channels allow asynchronous communication up to the buffer size.
Data Persistence Myth
Why Did My Deleted Item Come Back?
The Persistence Myth: RAM is volatile. Every go run starts a new process with fresh memory. Data doesn’t persist unless saved to a DB/File.
Many beginners are confused when they modify data (like a map) in their program, stop it, and find the changes gone when they restart.
Understanding Program Memory
- Each
go run creates a new process with fresh memory
- In-memory data structures (maps, slices, variables) only exist while the program runs
- When the program exits, all memory is released
The Solution
If you need data to persist:
// Save to file
func saveData(data map[string]string) error {
file, err := os.Create("data.json")
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
return encoder.Encode(data)
}
// Load from file
func loadData() (map[string]string, error) {
file, err := os.Open("data.json")
if err != nil {
return nil, err
}
defer file.Close()
var data map[string]string
decoder := json.NewDecoder(file)
err = decoder.Decode(&data)
return data, err
}
Loop Variable Gotcha
Capturing Loop Variables in Goroutines
When launching goroutines inside a loop, be careful about capturing the loop variable!
The Problem
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i) // All goroutines may print 5!
}()
}
The Solution
Pass the loop variable as an argument:
for i := 0; i < 5; i++ {
go func(n int) {
fmt.Println(n) // Each goroutine gets its own copy
}(i)
}
Always pass loop variables as arguments to goroutines to ensure each goroutine gets the correct value.
Summary
Most Go pitfalls stem from:
- Scope confusion - Variable shadowing with
:=
- Concurrency misunderstanding - Goroutines and channels behavior
- Memory model confusion - Volatility of RAM
- Closure capturing - Loop variables in goroutines
Understanding these concepts deeply will help you avoid 90% of common Go mistakes.