Wednesday 18 December 2019

dictionary - 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 ?
}


Answer



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.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...