When should you use any (interface{}), and when are generics the better choice?
Question 92MediumGo 1.22 to 1.25
any is an alias for interface{} (Go 1.18). Every type satisfies it, which is why it throws away static type information. Legitimate uses:
- Truly heterogeneous data:
fmt.Println(args ...any), decoded JSON (map[string]any),context.WithValue, loggers. - Reflection-driven APIs:
encoding/json,database/sqlscanning.
Use generics when the same algorithm works on many types and those types relate to each other, for example so that the output type matches the input type:
// any: caller must assert, errors at runtime, boxing allocations
func MaxAny(xs []any) any { /* ... type switch ... */ return nil }
// generics: type-safe, no boxing, no assertions
func Max[T cmp.Ordered](xs ...T) T {
m := xs[0]
for _, x := range xs[1:] {
m = max(m, x)
}
return m
}
fmt.Println(Max(3, 7, 2)) // 7
fmt.Println(Max("go", "rust")) // rust
Gotchas:
- A
[]stringis not a[]any. You must copy element by element, because the two have different memory layouts. - Numbers decoded from JSON into
anycome back asfloat64. - Putting non-pointer values in
anyusually allocates.
Rule of thumb: if you find yourself writing type switches over a fixed set of types, you probably want generics or a real interface with methods.
More on Interfaces, Methods & Embedding
- Q90What is interface embedding, and what changed in Go 1.14 about overlapping methods?
- Q91Why would you embed an interface inside a struct? What is the danger?
- Q93What does
var _ io.Writer = (*MyWriter)(nil)do, and why is it used? - Q94Go interfaces are satisfied implicitly. What design consequences follow, and where should interfaces be defined?
- Q95Explain "accept interfaces, return structs". When is returning an interface justified?
- Q96Why does Go favour small interfaces? Give examples of composition.