Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a type that can be used as a map key, but I want to prevent this from occurring. I assumed that if the type contained a private member it wouldn't be possible from other packages, but this appears to work anyway. What's the best way to make the type unusable as a map key?

type MyType struct {
    A *A
    b b

    preventUseAsKey ?
}
like image 828
Matt Joiner Avatar asked Jun 20 '16 07:06

Matt Joiner


People also ask

How do you make a good map key?

Put the symbol, followed by a dash and then the meaning of the symbol. As an example, a picture of a fireman's helmet, followed by a dash, followed by the words "Fire Station." Attach your key to the map somehow. If there's space on the map then put the key in a corner where it won't cover up any necessary information.

What is the main purpose of key on map?

A map legend or key is a visual explanation of the symbols used on the map. It typically includes a sample of each symbol (point, line, or area), and a short description of what the symbol means.


1 Answers

I don't see any benefit of disallowing a type being used as a key. It is just an option which may or may not be used, the type will not be any better or smaller or faster just because you forbid to use it as a map key.

But if you want to do it: 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.

So if you can violate the terms of the comparison operators, you implicitly get what you want. You have a struct, terms for the struct types:

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

So struct values are only comparable (and thus can only be used as keys in maps) if all their fields are comparable. Simply add a field whose type is not comparable.

Slice, map, and function values are not comparable.

So for example add a field whose type is a slice, and you're done:

type MyType struct {
    S             string
    i             int
    notComparable []int
}

Attempting to use the above MyType as a key:

m := map[MyType]int{}

You get a compile-time error:

invalid map key type MyType

Note:

I wrote about not having any benefit of forbidding the type being a key. It's more than that: from now on you won't be able to use comparison operators on values of your type anymore (because of the extra, non-comparable field), so e.g. you lose the option to compare those values:

p1, p2 := MyType{}, MyType{}
fmt.Println(p1 == p2)

Compile-time error:

invalid operation: p1 == p2 (struct containing []int cannot be compared)

Note that with a little trick you could still preserve the comparable nature of your type, e.g. by not exporting your type but a wrapper type which embeds the original one; and add the extra, non-comparable type to the wrapper type, e.g.:

type myType struct {
    S string
    i int
}

type MyType struct {
    myType
    notComparable []int
}

func main() {
    p1, p2 := MyType{}, MyType{}
    fmt.Println(p1.myType == p2.myType)
}

This way your myType can remain comparable but still prevent the exported, wrapper MyType type to be used as key type.

like image 186
icza Avatar answered Sep 22 '22 08:09

icza