How do you write methods on a generic type? What is wrong with func (s Stack[int]) Sum() int?
Question 125MediumGo 1.22 to 1.25
The receiver must list the type's parameters by name, and you may rename them. You cannot repeat or tighten the constraints in the receiver, and you cannot specialize a method for one type argument.
type Stack[T any] struct{ items []T }
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[E]) Len() int { return len(s.items) } // renaming is allowed
// TRAP: this compiles, but "int" is a NEW type parameter name shadowing
// the predeclared int. It is NOT a specialization for Stack[int].
func (s Stack[int]) Top() int {
return s.items[len(s.items)-1] // "int" here means T
}
Given Stack[string], Top() returns a string. If you write var total int = 0; total += x inside such a method, you get confusing errors, because int is now a type parameter constrained by any.
If you need behaviour that only exists for some type arguments, such as Sum on numbers, write a separate function with a narrower constraint:
func Sum[T cmp.Ordered | ~complex128](s *Stack[T]) T { var t T; for _, v := range s.items { t += v }; return t }
Interviewers like this question because the shadowing trap tells them who has actually written generic code.
More on Generics
- Q123What does this print? (type switch on a type parameter)
- Q124Why doesn't Go allow type parameters on methods? What do you do instead?
- 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? - Q128What are self-referential (recursive) constraints and generic interfaces? Give an example.
- Q129Why does
slices.Clonehave the signaturefunc Clone[S ~[]E, E any](s S) Sinstead offunc Clone[E any](s []E) []E?