Какие типы данных в 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)

Ответ 18+ 🔞

Да ты посмотри, какой вопрос подкинули! Прям как в том анекдоте про Герасима — «Муму? Муму!», а тут «ключ? ключ!». Ну ладно, разбираемся, блядь.

Вот смотри, в Go, если ты хочешь использовать что-то как ключ в map, то этот тип должен быть сравниваемым. То есть для него должны работать операции == и !=. Почему, спросишь? А потому что карта внутри — это хеш-таблица, ей нужно две вещи сделать: посчитать от ключа хеш (чтобы понять, в какую корзину пихать значение) и потом, если в одной корзине несколько ключей оказалось, уметь их сравнить, чтобы найти точное совпадение. Если тип несравниваемый — пиши пропало, нихуя не получится.

Что можно пихать в ключи (сравниваемые типы):

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

А что НЕЛЬЗЯ (несравниваемые типы):

  • Срезы ([]T): Абсолютно нельзя! Представь: у тебя два среза, они могут указывать на один и тот же массив, но длина разная. Или наоборот. Сравнивать их — это пиздец, головная боль. Go просто запрещает, и правильно делает.
  • Карты (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) // Компилятор тебе тут же: "Иди нахуй!"

Вот и вся магия, ёпта. Главное — запомни: если тип можно сравнить через ==, то его, скорее всего, можно и в ключи карты запихнуть. Если нельзя — то готовься к тому, что компилятор тебе мозги проест.