'Go function to create either new pointer or new value of a generic type
I have a function which takes a generic type and should return a function that always return a pointer. I.e. if you pass it a non-pointer type it should return a pointer to that type, if you pass it a pointer type it should return the same type. I don't want to use reflect.New as it's a performance-critical app.
I don't mind using reflection in the function that returns the factory function, however ideally not even there.
This is what I'm trying to do:
package main
import (
"fmt"
"reflect"
)
type Ptr[T any] interface {
*T
}
func makeNewA[T Ptr[U], U any]() any {
return new(U)
}
func makeNewB[T any]() any {
return new(T)
}
func makeNew[T any](v T) func() any {
if reflect.TypeOf(v).Kind() == reflect.Ptr {
return makeNewA[T] // <-- error: T does not match *U
} else {
return makeNewB[T]
}
}
type Foo struct{}
func main() {
make1 := makeNew(Foo{})
make2 := makeNew(&Foo{})
// should both return &Foo{}
fmt.Println(make1())
fmt.Println(make2())
}
Solution 1:[1]
This kind of conditional typing isn't nicely solved with generics, because when you instantiate T any with *Foo you lose information about the base type. As matter of fact your code still uses reflection and any (= interface{}), and the return type of the makeN functions will have to be type-asserted to *Foo.
The closest you can get with your current code is:
func makeNew[T any](v T) func() any {
if typ := reflect.TypeOf(v); typ.Kind() == reflect.Ptr {
elem := typ.Elem()
return func() any {
return reflect.New(elem).Interface() // must use reflect
}
} else {
return func() any { return new(T) } // v is not ptr, alloc with new
}
}
Then both maker functions will return an any that wraps a non-nil *Foo value:
fmt.Printf("%T, %v\n", make1(), make1()) // *main.Foo, &{}
fmt.Printf("%T, %v\n", make2(), make2()) // *main.Foo, &{}
Playground: https://gotipplay.golang.org/p/kVUM-qVLLHG
Further considerations:
return makeNewA[T]in your first attempt does not work because the conditionreflect.TypeOf(v).Kind() == reflect.Ptris evaluated at runtime, whereas instantiation ofmakeNewAhappens at compile-time. At compile-timeTis simply constrained byanyandany(=interface{}) doesn't implementPtr[U]- you can't capture information about both the pointer type and the base type with only the argument
v. For examplemakeNew[T Ptr[U], U any](v T)won't compile when called withmakeNew(Foo{})andmakeNew[T Ptr[U], U any](v U)will inferTas**Foowhen called with*Foo
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
