How do you write a generic function that needs to call a pointer-receiver method on T? (the PT interface{ *T; M() } pattern)
Question 109HardGo 1.22 to 1.25
type Setter interface{ Set(string) }
type Settable struct{ v string }
func (s *Settable) Set(v string) { s.v = v }
// Attempt 1: T Setter
func Bad[T Setter](xs []string) []T {
out := make([]T, len(xs))
for i, x := range xs {
out[i].Set(x) // T must be *Settable, so out[i] is a nil pointer: panic
}
return out
}
// Bad[Settable](...) // compile error: Settable does not satisfy Setter
// // (method Set has pointer receiver)
// Fix: a second type parameter constrained to "pointer to T that has Set"
type PtrSetter[T any] interface {
*T
Setter
}
func FromStrings[T any, PT PtrSetter[T]](xs []string) []T {
out := make([]T, len(xs))
for i, x := range xs {
PT(&out[i]).Set(x) // convert *T to PT, then call the method
}
return out
}
fmt.Println(FromStrings[Settable]([]string{"a", "b"})) // [{a} {b}]
The problem is that a constraint describes T's method set, but the method is on *T. If you instantiate with the pointer type, make([]T, n) gives you nil pointers and new(T) gives you a **Settable. The fix adds a type parameter PT whose type set is exactly *T and which also requires Set. Because PT's core type is *T, you can convert &out[i] to it.
The caller writes only FromStrings[Settable]: constraint type inference derives PT = *Settable from the *T element. This pattern is common in ORMs, decoders and protobuf helpers (for example, proto.Message constraints of the form interface{ *T; proto.Message }).
More on Interfaces, Methods & Embedding
- Q107What happens when you call a value-receiver method through an interface that holds a nil pointer?
- Q108Can you modify a struct stored inside an interface? What does this print?
- Q110How do you "seal" an interface so other packages cannot implement it? Can the seal be bypassed?
- Q111How do you check at runtime whether a type implements an interface using reflection? Why is
reflect.TypeOfawkward for interface types?