Какие типы данных в Go могут использоваться в качестве ключей в `map` и почему?

Ответ

Ключом в map в Go может быть любой тип данных, для которого определена операция сравнения на равенство (== и !=). Такие типы называются сравниваемыми (comparable).

Почему именно это требование?

Хеш-таблица (которой является map) должна уметь делать две вещи:

  1. Вычислять хеш от ключа, чтобы определить, в какой "корзине" (bucket) хранить значение.
  2. Сравнивать ключи внутри одной корзины, чтобы найти точное совпадение (на случай коллизий хешей).

В Go требование сравниваемости гарантирует, что для типа можно выполнить обе эти операции.

Разрешенные типы (сравниваемые):

  • Числовые типы: int, float64, complex128 и т.д.
  • Строки: string.
  • Булевы значения: bool.
  • Указатели: *T (сравниваются адреса, на которые они указывают).
  • Каналы: chan T (сравниваются указатели на внутреннюю структуру канала).
  • Интерфейсы: interface{} (сравниваются, если их динамические типы и значения сравнимы).
  • Структуры: struct, если все их поля являются сравниваемыми типами.
  • Массивы: [N]T, если тип элементов T является сравниваемым.

Запрещенные типы (несравниваемые):

  • Срезы ([]T): Нельзя, так как содержимое среза может измениться, даже если сам заголовок среза (указатель, длина, емкость) останется прежним. Сравнение указателей было бы бессмысленным.
  • Карты (map[K]V): Нельзя, так как они сами по себе не поддерживают операцию сравнения.
  • Функции (func()): Нельзя, так как для них не определена операция сравнения.

Пример:

// Корректные ключи
type MyStruct struct {
    ID int
    Name string
}

m := make(map[MyStruct]bool)
m[MyStruct{1, "test"}] = true

// Некорректный ключ - вызовет ошибку компиляции
// type BadStruct struct {
//     Data []int // Срез - несравниваемый тип
// }
// invalidMap := make(map[BadStruct]string)