Search code examples
dictionarygokeygo-gormhashable

map[gorm.DB]struct{}{} gives invalid map key type gorm.DB


I'd like to create a "set" of gorm types used in my application. So I'd like to define a map with my types gorm.DB as keys and empty structs{} as flags:

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

But compiler doesn't allow me do this with error: invalid map key type gorm.DB. I can fool it using pointers to gorm.DBs like:

map[*gorm.DB]struct{}

But it's not a solution, because I need to make it unique and if my map gets filled like db.AutoMigrate(&Chat{}) I can get lots of similar object with different addresses.

Another solution is to make a slice of gorm.DB:

autoMigrations []gorm.DB

But I have to filter elements on addition manually, which seems insane a little bit.


Solution

  • You can only use types as keys in a map that are comparable. Spec: Map types:

    The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice.

    gorm.DB is a struct, and struct values are only comparable if all their fields are comparable:

    Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

    But gorm.DB has e.g. a DB.values field which is a map type, and maps are not comparble, and thus gorm.DB values are not comparable either, and so you can't use it as map keys.

    If you want to create a set of types, you should use reflect.Type as the map keys, which you can acquire using reflect.TypeOf() from a value of that type.

    A little trick, if you want a reflect.Type without having to create a value of the type in question, you can start from a pointer value of that type (which may be nil), and use Type.Elem() to get the reflect.Type descriptor of the pointed type.

    For example, to get the reflect.Type descriptor of a struct type Point struct{ X, Y int } without actually creating / having a Point:

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

    Which prints main.Point. Try it on the Go Playground.

    See related questions:

    How can I prevent a type being used as a map key?

    Why can't Go slice be used as keys in Go maps pretty much the same way arrays can be used as keys?

    Set of structs in Go