"The bugs you don't understand are the ones that teach you the most."
Like slices, maps and channels contain internal pointers:
func modifyMap(m map[string]int) {
m["key"] = 100 // ✓ Caller sees it
}
func sendData(ch chan int) {
ch <- 42 // ✓ Works
}
func main() {
m := make(map[string]int)
modifyMap(m)
fmt.Println(m["key"]) // 100
ch := make(chan int)
go sendData(ch)
fmt.Println(<-ch) // 42
}
But: Reassigning the map/channel itself requires a pointer.
type Counter struct {
count int
}
// Value receiver — copies struct
func (c Counter) IncrementWrong() {
c.count++ // ❌ Modifies copy
}
// Pointer receiver — modifies original
func (c *Counter) IncrementRight() {
c.count++ // ✓ Modifies original
}
func main() {
c := Counter{count: 0}
c.IncrementWrong()
fmt.Println(c.count) // 0
c.IncrementRight()
fmt.Println(c.count) // 1
}
Guidelines:
m := map[string]int{"a": 1}
ptr := &m["a"] // ❌ Compile error!
Why? Maps relocate elements during growth, invalidating pointers.
Workaround: Store pointers in the map:
type Data struct { value int }
m := map[string]*Data{
"a": {value: 1},
}
m["a"].value = 100 // ✓ Works