Reputation: 821
Consider the following code snippet for version go1.18beta2 linux/amd64
type Vector[T comparable] struct {
data_ []T
}
func (v *Vector[T]) Contains(e T) bool {
for _, x := range v.data_ {
if x == e {
return true
}
}
return false
}
func TestVector(t *testing.T) {
v2 := Vector[Vector[int]]{}
}
This does not compile and gives error: “Vector[int] does not implement comparable”
simply because Vector
has no equality operators defined. However, I cannot find how to define them.
Question: Is this approach of creating a comparable struct not allowed, and why; or is the documentation not written yet?
Upvotes: 5
Views: 9852
Reputation: 44647
The constraint comparable
is predeclared and supported by the language specifications. You can't "manually" make a type implement it. The documentation is available in the specs (under Type Constraints):
The predeclared interface type comparable denotes the set of all concrete (non-interface) types that are comparable. Specifically, a type T implements comparable if:
T
is not an interface type andT
supports the operations == and !=; orT
is an interface type and each type inT
's type set implements comparable.
Your type Vector[T comparable]
doesn't meet any of those conditions. It is not an interface type, and it does not otherwise support the equality operations, because one of its fields data_ []T
is not comparable due to being a slice — even if element type is constrained by comparable
.
The purpose of the comparable
constraint is really just to allow writing generic code with ==
and !=
operators. If a type is not comparable by design, you can't write such code. That would be true even if Vector
didn't have a type parameter.
If your goal is instantiating Vector[Vector[T]]
and allow equality tests between instances of Vector[T]
, you might want to add an Equal
method that takes care of this specific use case — only vectors instantiated with the same type parameter as the receiver will be allowed:
func (v *Vector[T]) Equal(e Vector[T]) bool {
// test equality in a way that makes sense for this type
}
Worth mentioning that there is a way to make Vector[T comparable]
comparable itself, i.e. change the data_
field to be a pointer-to-slice:
type Vector[T comparable] struct {
data_ *[]T
}
Now instantiation with Vector[Vector[int]]
compiles. However, beside being very cumbersome to initialize with struct literals (playground), it comes with all caveats of pointer comparison. More specifically:
Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
Now the comparison x == e
tests that the memory address stored in the data_
field in x
and e
is the same. This might skew the semantics of comparing two Vector[T]
instances — is it correct to say that two vector instances are equal if they hold a reference to the same slice? Maybe. It depends on the assumptions your program wants to make. Personally, I don't think this is actually better than having a separate Equal
method and/or redesigning your data types, but as usual, YMMV.
Note also that if you instantiate as Vector[float64]
and compare NaN
values, the comparison will be false.
Upvotes: 8