When should you NOT use generics?
Question 131MediumGo 1.22 to 1.25
Interviewers want judgment, not enthusiasm. Guidance, broadly following Ian Lance Taylor's "When To Use Generics":
- When you only call methods: if the function just calls
r.Read(), take anio.Reader. A type parameter adds nothing. - When the implementation differs per type: use an interface with methods. Do not type-switch inside generic code.
- When the operation needs reflection anyway: for example,
encoding/jsonstyle struct walking. - Premature abstraction: write concrete code first. Introduce type parameters when you find yourself duplicating the same code for several types.
- Hot paths with pointer-shaped method calls: they can be slower (see GC shapes).
- Public APIs: a type parameter becomes part of the API. Changing its constraint later is a breaking change.
// Over-engineered
func WriteAll[W io.Writer](w W, data []byte) error { _, err := w.Write(data); return err }
// Idiomatic
func WriteAll2(w io.Writer, data []byte) error { _, err := w.Write(data); return err }
Good uses:
- General-purpose data structures: trees, heaps, sets, caches.
- Functions over slices, maps and channels of any element type:
slices.Index,Merge, fan-in. - Numeric algorithms.
- Type-safe wrappers such as
atomic.Pointer[T]or a typedsync.Pool.
More on Generics
- 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?
- Q132Implement a generic stack and a generic FIFO queue. What are the gotchas?
- Q133Implement a generic binary search tree keyed by
cmp.Orderedwith an in-order iterator. - Q134Implement a generic
Set[T]with union, intersection and iteration. - Q135Design a thread-safe generic cache with a
GetOrLoadmethod. What concurrency pitfalls exist?