Writing generic collection types in Go: the missing documentation
-
Context:
- Generics were released in Go 1.18 (~2y), is not used very much - only in some good cases for methods in slices and maps packages.
-
Problem:
- Wanted to write a sortable Set (slice or map) of any type but the documents for a generic collection are poor and immature.
type Sortable[T comparable] interface { Less(member T) bool } type Name struct { First string Last string } func (n Name) Less(member Name) bool { return n.First < member.First || n.First == member.First && n.Last < member.Last } var _ Sortable[Name] = Name{}
type SortableSet[T Sortable] interface { // cannot use generic type Sortable[T comparable] without instantiation Add(member T) Size() int Contains(member T) bool Sorted() []T }
- Can try what built-in slices does in slices.go
func Index[S ~[]E, E comparable](s S, v E) int { for i := range s { if v == s[i] { return i } } return -1 }
type SortableSet[T Sortable[E], E comparable] interface { Add(member T) Size() int Contains(member T) bool Sorted() []T }
- But not work for map:
type MapSet[T Sortable[E], E comparable] struct { members map[T]struct{} // invalid map key type T (missing comparable constraint) }
-
Solution:
// Type set used only for constraints, not vars type SortableConstraint[T comparable] interface { comparable Sortable[T] } type SortableSet[T SortableConstraint[T]] interface { Add(member T) Size() int Contains(member T) bool Sorted() []T } type SliceSet[T SortableConstraint[T]] struct { members []T } type MapSet[T SortableConstraint[T]] struct { members map[T]struct{} }
-
Conclusion:
-
Current Google rearch results are not really helpful since the immaturity
-
The official Go documentations (proposal/ language spec) are too long
-
This article is very handy as considerably pioneering in real life implementation at @Dolt
-
Cheat sheet found in spec by the author:
type argument type constraint // constraint satisfaction int interface{ ~int } // satisfied: int implements interface{ ~int } string comparable // satisfied: string implements comparable (string is strictly comparable) []byte comparable // not satisfied: slices are not comparable any interface{ comparable; int } // not satisfied: any does not implement interface{ int } any comparable // satisfied: any is comparable and implements the basic interface any struct{f any} comparable // satisfied: struct{f any} is comparable and implements the basic interface any any interface{ comparable; m() } // not satisfied: any does not implement the basic interface interface{ m() } interface{ m() } interface{ comparable; m() } // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }
-
Russ Cox is working on a bot
-
Context:
-
As there is @gopherbot to try to help automate: labeling, commenting, changing issues' status...
-
Gaby (Go AI Bot) is being built (experiment) for what LLMs can be used effectively (including identifying what they should not be used for).
-
Source is not official nor open, can help snoop around in this search and help send feedback.
-