Let's say I want to write a generic List type with some useful methods, for example:
type List[T any] []T
func (l *List[T]) Len() int
func (l *List[T]) Get(pos int) (t T, err error)
func (l *List[T]) Set(pos int, t T) error
func (l *List[T]) Append(t ...T)
func (l *List[T]) Insert(t T, pos int) error
func (l *List[T]) Remove(pos int) (t T, err error)
// etc...
However, there are other useful methods that might require the list's element type T to be further constrained. For example, we can't implement a Contains method on this List type:
func (l *List[T]) Contains(t T) bool {
for _, s := range *l {
if s == t { // compiler error: invalid operation: s == t (incomparable types in type set)
return true
}
}
return false
}
We can only implement Contains if we declare List as
type List[T comparable] []T
But then this makes it impossible to make a List of a non-comparable type.
Is there a way to get the best of both worlds here? i.e. have a List[T] that can be used for non-comparable types T, but allow it to have a Contains method in the case that T is comparable?
I've thought of:
- using different types (e.g.
UncomparableList/ListorList/ComparableList) - making
Containsa function rather than a method
but I don't really like either of these.
Go doesn't have specialization so I don't think you can make it work exactly like this (not a generics expert, though).
I think a reasonably Go-ish way to go about this is to pass in an explicit comparator:
Then you can do
For comparable types you could provide a default:
So the above becomes
You could also embed a comparator inside the
Listtype and give it a default offunc(a, b T) bool { return false }and expose a constructor that you can pass a custom comparator into. But that might be too implicit for your liking.