map[gorm.DB]struct{}{} дает неверный тип ключа карты gorm.DB

Я хотел бы создать «набор» типов гормов, используемых в моем приложении. Поэтому я хотел бы определить map с моими типами gorm.DB в качестве ключей и пустой structs{} в качестве флагов:

var (
    autoMigrations map[gorm.DB]struct{}
)

Но компилятор не позволяет мне сделать это с ошибкой: invalid map key type gorm.DB. Я могу обмануть его, используя указатели на gorm.DB, например:

map[*gorm.DB]struct{}

Но это не решение, потому что мне нужно сделать его уникальным, и если моя карта будет заполнена как db.AutoMigrate(&Chat{}), я могу получить много похожих объектов с разными адресами.

Другое решение — сделать срез gorm.DB:

autoMigrations []gorm.DB

Но мне приходится фильтровать элементы при добавлении вручную, что немного безумно.


person Eugene Lisitsky    schedule 08.11.2017    source источник


Ответы (1)


В качестве ключей карты можно использовать только те типы, которые сопоставимы. Спецификация: Типы карт:

операторы сравнения == и != должны быть полностью определены для операндов типа ключа; таким образом, тип ключа не должен быть функцией, картой или срезом.

gorm.DB — это структура, а значения структур сопоставимы только в том случае, если сопоставимы все их поля:

Значения структуры сравнимы, если все их поля сравнимы. Два значения структуры равны, если равны их соответствующие не-пустые поля.

Но gorm.DB имеет, например. поле DB.values, которое является типом карты, а карты несопоставимы, и, следовательно, значения gorm.DB также несопоставимы, и поэтому вы не можете использовать его в качестве ключей карты.

Если вы хотите создать набор типов, вы должны использовать reflect.Type в качестве ключей карты, которые вы можете получить, используя reflect.TypeOf() из значения этого типа.

Небольшой трюк: если вам нужен reflect.Type без создания значения рассматриваемого типа, вы можете начать со значения указателя этого типа (которое может быть nil) и использовать Type.Elem() для получения дескриптора reflect.Type указанного типа. .

Например, чтобы получить дескриптор reflect.Type структуры типа Point struct{ X, Y int }, фактически не создавая/не имея Point:

type Point struct{ X, Y int }
tpoint := reflect.TypeOf((*Point)(nil)).Elem()
fmt.Println(tpoint)

Что печатает main.Point. Попробуйте его на Go Playground.

Смотрите связанные вопросы:

Как я могу предотвратить тип используется в качестве ключа карты?

Почему фрагмент Go нельзя использовать в качестве ключей в картах Go почти так же, как массивы можно использовать в качестве ключей?

Набор структур в Go

person icza    schedule 08.11.2017
comment
Отличный ответ! Есть ли у Go способ определить собственную функцию сравнения? Нравится __hash__ в Python? таким образом, я могу сделать их сопоставимыми. - person Eugene Lisitsky; 08.11.2017
comment
@EugeneLisitsky Нет, это не так. Вы не можете написать собственные функции сравнения, которые будут автоматически использоваться средой выполнения Go. - person icza; 08.11.2017