The Problem
It’s a common scenario: you have a struct stored in a map and need to pass it to a function that modifies data via a pointer. You might try to use the address-of operator (&) directly on the map entry. It feels intuitive, but the compiler won't allow it.
type User struct {
Name string
Age int
}
m := map[string]User{
"id123": {Name: "Alice", Age: 30},
}
// This line triggers the compiler error
updateUser(&m["id123"])
The compiler stops you immediately with a specific message:
./main.go:15:13: cannot take the address of m["id123"]
You will see this same roadblock when trying to grab the address of a function's return value, such as &getSomeValue(). Both fail for the same underlying reason: addressability.
Why Go Blocks This
Go map values are not addressable because maps are dynamic engines. Under the hood, Go stores map data in buckets. When a map grows—specifically when it hits a load factor of roughly 6.5—it triggers a "rehash." During this process, Go allocates a new set of buckets and moves existing elements to new memory locations.
If Go let you take the address of a map value, you would hold a pointer to a specific memory slot. The moment the map grows and evacuates that bucket, your pointer would point to old, invalid data. This would lead to non-deterministic bugs that are incredibly hard to track down. To ensure memory safety, the Go team made map elements unaddressable by design.
Function results are restricted for a different reason. A return value is often a temporary "rvalue" living in a CPU register or a transient stack location. Taking its address is risky because the storage for that value may vanish as soon as the line of code finishes executing.
How to Fix It
Solution 1: The Temporary Variable
The quickest fix is to move the map value into a local variable. Variables on the stack are stable and addressable.
// 1. Extract the value to a local variable
user := m["id123"]
// 2. Pass the address of the variable
updateUser(&user)
// 3. Write the changes back to the map
m["id123"] = user
This approach is safe but involves two copies. If your struct is massive—say, a 10KB blob—doing this thousands of times per second might impact performance.
Solution 2: Use a Map of Pointers
If you need to modify map values frequently, change your map definition to store pointers instead of raw structs. This is the industry standard for mutable collections in Go.
// Change map[string]User to map[string]*User
m := map[string]*User{
"id123": &User{Name: "Alice", Age: 30},
}
// Access the pointer directly without the & operator
val := m["id123"]
updateUser(val)
When the map rehashes, it moves the pointers. The actual User struct stays exactly where it was in memory. This eliminates the need for copying and bypasses addressability issues entirely.
Solution 3: Handling Function Results
For function results like &foo.GetStatus(), the fix is identical: assign the result to a variable first to give it a permanent home on the stack.
// Fails: &someFunction()
// Works:
val := someFunction()
result := &val
Verification
Run go build. If the error is gone, the compiler is satisfied. However, if you chose Solution 1, double-check your logic. A frequent mistake is updating the local variable but forgetting to re-assign it to the map. Without that final assignment, your map stays unchanged.
temp := m["key"]
temp.Age = 31
// Without m["key"] = temp, the map still stores Age: 30
m["key"] = temp
The Takeaway
- **Maps are volatile:** Elements move during growth, so their memory addresses are never permanent.
- **Safety first:** Go’s addressability rules exist to prevent dangling pointers and memory corruption.
- **Choose the right map type:** Use `map[K]V` for read-heavy, small data. Use `map[K]*V` when you need to update fields or handle large structs efficiently.

