Перейти к карте с определяемым пользователем ключом с определяемым пользователем равенством?

Предположим, что у меня есть тип структуры в Go, который я хочу использовать в качестве ключа на карте, но я не хочу использовать операцию Go, встроенную в равенство. Какой лучший способ построить такую ​​карту?

Для конкретного примера, вот мой тип ключа и операция равенства:

type Key struct {
    a *int
}

func Equal(x Key, y Key) bool {
    return *x.a == *y.a
}

Как создать карту, использующую Equal для сравнения ключей?

Ответ 1

Go имеет строго сопоставимую семантику для значений, используемых в качестве ключей карты. Таким образом, вы не можете определить свой собственный хэш-код и функции равенства для ключей карты, как это можно сделать во многих других языках.

Однако рассмотрим следующий обходной путь. Вместо того чтобы использовать экземпляры структуры непосредственно в качестве ключей, используйте производный атрибут структуры, который по своей природе может использоваться в качестве ключа и имеет желаемую семантику равенства. Часто просто получить целое или строковое значение в качестве хеш-кода, который служит идентификатором для экземпляра.

Например:

type Key struct {
  a *int
}

func (k *Key) HashKey() int {
  return *(*k).a
}

k1, k2 := Key{intPtr(1)}, Key{intPtr(2)}
m := map[int]string{}
m[k1.HashKey()] = "one"
m[k2.HashKey()] = "two"
// m = map[int]string{1:"one", 2:"two"}
m[k1.HashKey()] // => "one"

Конечно, неизменность является критической проблемой при таком подходе. В приведенном выше примере, если вы измените поле a экземпляр больше не будет использоваться в качестве хеш-ключа, поскольку его идентификатор изменился.

Ответ 2

Это невозможно в Go. Нет перегрузки операторов или метода "Равенство", которые вы можете переопределить (из-за отсутствия наследования от общего базового класса, как в .NET, о котором мне напоминает ваш пример). Этот ответ содержит больше информации о сравнениях на равенство, если вам интересно; Можно ли определить равенство для именованных типов/структур?

Как уже упоминалось в комментариях, если вы хотите сделать что-то вроде этой работы, я бы рекомендовал использовать свойство объекта в качестве ключа. Вы можете определить равенство, основываясь на том, как вы устанавливаете значение этого свойства (например, это может быть контрольная сумма байтов объектов или что-то еще, если вы ищете членное равенство).