What are self-referential (recursive) constraints and generic interfaces? Give an example.
Question 128HardGo 1.22 to 1.25
An interface can itself have type parameters. That lets a constraint refer to the type being constrained, similar to F-bounded polymorphism or Java's Comparable<T>. It is useful when a method must take or return the same concrete type.
type Lesser[T any] interface {
Less(T) bool
}
// T must have a method Less(T) bool
func MinOf[T Lesser[T]](xs ...T) T {
m := xs[0]
for _, x := range xs[1:] {
if x.Less(m) {
m = x
}
}
return m
}
type Version struct{ Major, Minor int }
func (a Version) Less(b Version) bool {
return a.Major < b.Major || (a.Major == b.Major && a.Minor < b.Minor)
}
fmt.Println(MinOf(Version{1, 4}, Version{1, 2}, Version{2, 0})) // {1 2}
A generic interface with only methods, such as type Store[K comparable, V any] interface{ Get(K) (V, bool); Put(K, V) }, can also be used as an ordinary interface type once it is instantiated (var s Store[string, int]). That makes it useful for dependency injection in repositories and caches.
Gotcha: Less(Lesser[T]) bool would be a different signature. The parameter type must be exactly T.
More on Generics
- Q126Name the restrictions around type parameters: embedding, type declarations, and method sets.
- Q127Explain the "pointer method" constraint pattern (
PT interface{ *T; M() }). Why is it needed? - Q129Why does
slices.Clonehave the signaturefunc Clone[S ~[]E, E any](s S) Sinstead offunc Clone[E any](s []E) []E? - Q130How does the Go compiler implement generics (GC shape stenciling with dictionaries)? What are the performance implications?
- Q131When should you NOT use generics?
- Q132Implement a generic stack and a generic FIFO queue. What are the gotchas?