В Go запрещено брать указатель (ссылку) на значение, хранящееся в map, и это ограничение имеет несколько важных причин, связанных с внутренним устройством map и безопасностью работы с памятью.
Когда map растет (при достижении определенного load factor), происходит рехеширование и перемещение элементов в новые корзины. В этот момент:
m := make(map[int]int)
m[1] = 100
// Если бы можно было сделать:
ptr := &m[1] // Опасность!
// При рехешировании ptr может стать невалидным
Адрес значения может измениться, что сделает ранее полученный указатель недействительным.
Map в Go — это хеш-таблица с динамической структурой:
Разрешение указателей на значения map могло бы привести к:
m := make(map[int]int)
m[1] = 100
// Копируем значение
val := m[1]
// Работаем с копией
modifyValue(&val)
// Возвращаем обратно в map
m[1] = val
m := make(map[int]*int)
val := 100
m[1] = &val // Теперь map хранит указатель
// Можно модифицировать значение через указатель
*m[1] = 200
Но будьте осторожны с этим подходом:
Для сравнения, со срезами (slices) можно брать указатели на элементы:
s := []int{1, 2, 3}
ptr := &s[1] // Это разрешено
Потому что:
При компиляции Go явно запрещает взятие адреса элементов map:
// Этот код не скомпилируется:
m := make(map[int]int)
m[1] = 100
ptr := &m[1] // compiler error: cannot take address of m[1]
Ошибка компилятора: "cannot take the address of m[1]"
type wrapper struct{ val int }
m := make(map[int]wrapper)
// m[1].val = 10 // Нельзя модифицировать поле напрямую
w := m[1]
w.val = 20
m[1] = w
import "sync"
var m sync.Map // concurrent-safe map
m.Store(1, 100)
if val, ok := m.Load(1); ok {
newVal := val.(int) + 1
m.Store(1, newVal)
}