Go

What is addressability, and why can't you call a pointer method on a map element?

Question 29HardGo 1.22 to 1.25

An expression is addressable if you can take its address with &. That includes variables, pointer dereferences, slice elements, and fields or elements of addressable structs and arrays. These are not addressable:

  • map elements
  • function return values
  • constants and literals (except that &T{} is allowed as special syntax)
  • results of a type assertion
  • string bytes
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ }

m := map[string]Counter{"a": {}}
// m["a"].Inc()     // compile error: cannot call pointer method Inc on Counter
// m["a"].n = 5     // compile error: cannot assign to struct field m["a"].n in map
c := m["a"]; c.Inc(); m["a"] = c // copy, change, write back

mp := map[string]*Counter{"a": {}}
mp["a"].Inc()        // OK: the element is a pointer

s := []Counter{{}}
s[0].Inc()           // OK: slice elements are addressable

// Counter{}.Inc()   // compile error: a composite literal value is not addressable
(&Counter{}).Inc()   // OK
// newCounter().Inc() // compile error if newCounter returns Counter (not *Counter)

Why map elements can't be addressed: when a map grows, the runtime moves its entries to new memory. This is true of both the old bucket-based map and the Swiss-table map that has been the default since Go 1.24, where tables split and rehash as they grow. A pointer to an entry would be left pointing at stale memory, or at a slot that now holds a different key. Slices don't have this problem. A pointer to a slice element always points into a live backing array. After an append that reallocates, the old pointer still refers to the old array, which the GC keeps alive.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions