Back to blog

Saturday, August 15, 2026

Why Can't I Take the Address of a Map Value in Go?

Why Can't I Take the Address of a Map Value in Go?

Why Can't I Take the Address of a Map Value in Go?

Because m[k] is not addressable. The compiler rejects &m[k] (and pointer-receiver calls on m[k]) so you cannot hold a pointer into a hash table that may move. Copy the value into a variable, change it, assign it back. If you need in-place mutation, store *T in the map instead of T.

The failure

package main

type User struct {
    Name  string
    Hits  int
}

func (u *User) Hit() { u.Hits++ }

func main() {
    users := map[string]User{
        "ada": {Name: "Ada"},
    }

    // cannot take address of users["ada"]
    // p := &users["ada"]

    // cannot call pointer method on users["ada"]
    // users["ada"].Hit()

    users["ada"].Hits++ // also illegal: cannot assign to struct field users["ada"].Hits
}

users["ada"] += … for a number is allowed only because it is sugar for copy, add, store. It is not an in-place update of a stable slot.

Why this happens

A map value lives in a bucket. Inserts, deletes, and growth reorganize those buckets. Keith Randall (who implemented Go maps) put it plainly: if you kept a pointer into a bucket and the map grew, that pointer would name an old bucket. Go will not let you write that dangling pointer.

Slice elements are different. They sit in a backing array at a fixed offset until append allocates a new array. The language treats &s[i] as addressable; it does not treat &m[k] that way. After delete(m, k), there is also no honest answer for what a leftover pointer should name without breaking memory safety.

The spec rule is the addressability list: variables, pointer indirection, slice/array elements, struct fields of addressable structs, and composite literals. A map index is none of those. Same family as &42 and &f().

The fix

Copy, mutate, write back — works for any value type:

u := users["ada"]
u.Hit()
users["ada"] = u

Store pointers when many call sites need to mutate:

users := map[string]*User{
    "ada": {Name: "Ada"},
}
users["ada"].Hit() // the map entry is the pointer; the User does not move

A missing key still returns nil. Check before you dereference, or you have a nil-pointer panic instead of a compile error.

Do not use a pointer into a map as a long-lived identity for a record. The map is a table, not an object graph. If identity matters, keep the *User (or an ID) as the value.