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 ?
}
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.
Your type should not be comparable in order to be unfit as a map key.
Slice, map, and function values are not comparable
See Key Type:
Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==
, and may not be used as map keys.
So if your type is a slice, map or function, you should get what you need.
It could be an "alias" (defining a new named type):
type StringSliceWrap []string
type MyFunc func(i int)
That alias would not be used as a map key.
Update 2017: Brad Fitzpatrick give this tip (adding a slice in your struct
) to make sure your type struct
is not comparable: See play.golang.org:
package main
// disallowEqual is an uncomparable type.
// If you place it first in your struct, you prevent == from
// working on your struct without growing its size. (Don't put it
// at the end; that grows the size of the struct)
type disallowEqual [0]func()
type T struct {
_ disallowEqual
Foo string
Bar int
}
func main() {
var t1 T
var t2 T
println(t1 == t2)
}
T
cannot be used as amp key now!