Ответ
Ключом в map в Go может быть любой тип данных, для которого определена операция сравнения на равенство (== и !=). Такие типы называются сравниваемыми (comparable).
Почему именно это требование?
Хеш-таблица (которой является map) должна уметь делать две вещи:
- Вычислять хеш от ключа, чтобы определить, в какой "корзине" (bucket) хранить значение.
- Сравнивать ключи внутри одной корзины, чтобы найти точное совпадение (на случай коллизий хешей).
В 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) // Компилятор тебе тут же: "Иди нахуй!"
Вот и вся магия, ёпта. Главное — запомни: если тип можно сравнить через ==, то его, скорее всего, можно и в ключи карты запихнуть. Если нельзя — то готовься к тому, что компилятор тебе мозги проест.