# Golang Range Loop Reference - Why Your Loop Keeps Giving You the Same Pointer (and How to Fix It)
Table of Contents
When I first started learning Go, I thought I was doing everything right—until I ran into a weird bug about golang range loop reference. I was iterating over a list of Book structs (of course, I can’t share the real structs and code used here… all here are for turorial purpose), taking the pointer of each one, and storing them into a slice. But at the end of the loop, all the pointers pointed to… the same book?! 🤯
Let’s walk through this classic Go beginner mistake together — and fix it the right way.
📚 The Use Case: A Slice of Books in a Library
Suppose we have a list of books, and we want to collect pointers to each one so we can modify them later.
Here’s the code I thought would work:
for _, book := range books { bookPointers = append(bookPointers, &book) // Oops...}But when I printed out the pointers, they all pointed to the last book in the list. This bug stumped me for a while until I understood one critical Go behavior.
The File Structure To Run The Code
learning-golang/├── 01-loop-reference-pitfall/│ ├── main.go│ └── README.md├── Makefile├── bin/└── go.modThis is the complete buggy code:
package main
import ( "fmt")
type Book struct { Title string Author string}
func main() { originalBooks := []Book{ {"Go in Action", "William Kennedy"}, {"The Go Programming Language", "Alan Donovan"}, {"Introducing Go", "Caleb Doxsey"}, }
fmt.Println("❌ Buggy Version:") var buggyPointers []*Book for _, book := range originalBooks { buggyPointers = append(buggyPointers, &book) } for _, bp := range buggyPointers { fmt.Printf("Title: %-30s | Address: %p\n", bp.Title, bp) }}The Makefile:
# Usage:# make run DIR=01-loop-reference-pitfall# make build DIR=01-loop-reference-pitfall# make clean
GO=go
run: @echo "👉 Running $(DIR)/main.go..." cd $(DIR) && $(GO) run main.go
build: @echo "🔧 Building binary from $(DIR)/main.go..." cd $(DIR) && $(GO) build -o ../bin/$(notdir $(DIR))
clean: @echo "🧹 Cleaning up built binaries..." rm -rf bin/Build and Run The Code
❯ go mod init github.com/geekcoding101/learning-golang❯ make run DIR=01-loop-reference-pitfall
As you see, the Address didn’t change at all!
🐛 The Problem: Reuses the Loop Variable in Golang Range Loop Reference
In Go, when you do:
for _, book := range booksThe book variable is reused in every iteration. It’s not a new instance each time. So taking &book actually gives you the same memory address over and over.
This means every pointer in the slice is just pointing to the same memory location, which at the end holds the value of the last book.
✅ The Fix: Indexing Directly
The correct way is to use an index:
fmt.Println("\n✅ Fixed Version:")var fixedPointers []*Bookfor i := range originalBooks { fixedPointers = append(fixedPointers, &originalBooks[i])}for _, bp := range fixedPointers { fmt.Printf("Title: %-30s | Address: %p\n", bp.Title, bp)}Now, each pointer actually refers to the corresponding element in the original slice. Problem solved!

💻 Real Code Example on GitHub
I’ve documented this bug and fix in my GitHub repository:
👉 github.com/geekcoding101/learning-golang
Here’s what you’ll find:
-
The buggy version (with all pointers pointing to the same book)
-
The fixed version (each pointer is correct)
-
A
Makefileto help you run and build each learning topic
✍️ Follow My Golang Tutorials
I’ll continue sharing these hands-on lessons as I deepen my understanding of Go.
Check out my blog 👉 www.geekcoding101.com — where I share practical posts, breakdowns, and real-world insights from my coding journey.
📌 Quick Hashtag
#Golang Range Loop Reference, #for loop golang range, #for loop range golang, #golang for range loop, #for loop in golang with range, #go slice reference, #go for loop pointer trap, #why does my go loop store the same pointer, #golang how to correctly get pointer from loop, #go for loop pointer always same