Home > Article > Backend Development > How to instantiate a non-zero pointer to a type parameter using a generic Go?
php editor Xigua will introduce to you how to use non-zero pointers of generic instantiation type parameters in the Go language. In Go language, generics are a powerful feature that can increase the flexibility and reusability of code. When we need to instantiate a non-zero pointer in a generic function or method, we can use type assertions and reflection to achieve this. By using these techniques, we can create a non-zero pointer instance at runtime based on the specific type of the type parameter, thereby achieving the flexibility and versatility of generics. Let's take a closer look at the specific implementation method.
Now that type parameters are available on golang/go:master
, I decided to give it a try. It seems I've hit a limitation that I can't find in the type parameters proposal. (Or I must have missed it).
I want to write a function that returns a slice of generic type values with interface type constraints. If the type passed is an implementation with a pointer receiver, how do we instantiate it?
type SetGetter[V any] interface { Set(V) Get() V } // SetGetterSlice turns a slice of type V into a slice of type T, // with T.Set() called for each entry in values. func SetGetterSlice[V any, T SetGetter[V]](values []V) []T { out := make([]T, len(values)) for i, v := range values { out[i].Set(v) // panic if T has pointer receiver! } return out }
When the above SetGetterSlice()
function is called using the *Count
type as T
, this code will call Set(v)## Confusion occurs when #. (Go2go Playground) Not surprising since basically the code creates a slice of
nil pointers:
// Count implements SetGetter interface type Count struct { x int } func (c *Count) Set(x int) { c.x = x } func (c *Count) Get() int { return c.x } func main() { ints := []int{1, 2, 3, 4, 5} sgs := SetGetterSlice[int, *Count](ints) for _, s := range sgs { fmt.Println(s.Get()) } }Variation of the same question
will cause compilation to fail because it returns
*T, where the type checker expects to see
T.
compiles but causes the same runtime panic because
new(T) returns
**Count here In this case, the pointer to
Count is still
nil.
will cause compilation to fail:
func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T { out := make([]*T, len(values)) for i, v := range values { out[i] = new(T) out[i].Set(v) // panic if T has pointer receiver } return out } func main() { ints := []int{1, 2, 3, 4, 5} SetGetterSlice[int, Count](ints) // Count does not satisfy SetGetter[V]: wrong method signature }
func F(T interface{})() []T is perfectly valid syntax, why is this needed?
func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T { out := make([]T, len(values)) for i, v := range values { out[i] = constructor() out[i].Set(v) } return out } // ... func main() { ints := []int{1, 2, 3, 4, 5} SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) }) }Summary
T convertible to its pointer type. In its most basic form, the technique looks like this (with anonymous constraints):
func Foo[T any, PT interface { *T; M() }]() { p := PT(new(T)) p.M() // calling method on non-nil pointer }Playground:
https://www.php.cn/link/24aef8cb3281a2422a59b51659f1ad2e
Step-by-step solution
Your constraintsSetGetter already declare the type parameter
V, so we modify the above example slightly:
// V is your original type param // T is the additional helper param type SetGetter[V any, T any] interface { Set(V) Get() V *T }Then define the
SetGetterSlice function, whose type parameter is
T any, whose purpose is only to instantiate the constraint
SetGetter.
&out[i] to a pointer type and successfully call the method on the pointer receiver:
// T is the type with methods with pointer receiver // PT is the SetGetter constraint with *T func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T { out := make([]T, len(values)) for i, v := range values { // out[i] has type T // &out[i] has type *T // PT constraint includes *T p := PT(&out[i]) // valid conversion! p.Set(v) // calling with non-nil pointer receiver } return out }Full program:
CFE57E536C89530D9A8C38E10967A10D
This becomes more verbose becauseSetGetterSlice now requires three type parameters: the original
V plus
T (the type with the pointer receiver) and
PT (new constraints). However, when you call the function, you can omit the third - via type inference, the type parameters
V and ## required to instantiate
PT SetGetter[V,T] #T
are all known:
<pre class="brush:php;toolbar:false;">SetGetterSlice[int, Count](ints)</pre>
Playground:
The above is the detailed content of How to instantiate a non-zero pointer to a type parameter using a generic Go?. For more information, please follow other related articles on the PHP Chinese website!