Why does slices.Clone have the signature func Clone[S ~[]E, E any](s S) S instead of func Clone[E any](s []E) []E?
Question 129HardGo 1.22 to 1.25
The goal is to preserve the caller's named slice type. With []E in and []E out, a named type like type IDs []int comes back as a plain []int, and its methods are lost. With S ~[]E, S is inferred as IDs, and constraint type inference derives E = int from the core type.
type IDs []int
func (ids IDs) Sum() (t int) {
for _, v := range ids {
t += v
}
return
}
func CloneBad[E any](s []E) []E { return append([]E(nil), s...) }
func main() {
ids := IDs{1, 2, 3}
fmt.Println(slices.Clone(ids).Sum()) // 6: returns IDs
// CloneBad(ids).Sum() // compile error: []int has no method Sum
var back IDs = CloneBad(ids) // assignable ([]int is unnamed), but needs a variable
_ = back
}
The same pattern appears across slices and maps: func Keys[Map ~map[K]V, K comparable, V any]. If your API takes and returns a collection, use S ~[]E. Interviewers are checking that you understand both constraint type inference and API ergonomics.
More on Generics
- 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.
- 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?
- Q133Implement a generic binary search tree keyed by
cmp.Orderedwith an in-order iterator.