英文:
Go function to create either new pointer or new value of a generic type
问题
我有一个函数,它接受一个泛型类型,并应返回一个始终返回指针的函数。也就是说,如果你传递给它一个非指针类型,它应返回该类型的指针;如果你传递给它一个指针类型,它应返回相同的类型。我不想使用reflect.New
,因为这是一个性能关键的应用程序。
在返回工厂函数的函数中,我不介意使用反射,但最好不要在那里使用。
这是我尝试做的事情:
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())
}
英文:
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())
}
答案1
得分: 2
这种条件类型的问题不适合使用泛型来解决,因为当你用*Foo
实例化T any
时,会丢失关于基本类型的信息。事实上,你的代码仍然使用了反射和any
(即interface{}
),而makeN
函数的返回类型将需要进行类型断言为*Foo
。
你目前的代码可以得到最接近的结果是:
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() // 必须使用反射
}
} else {
return func() any { return new(T) } // v 不是指针,使用 new 进行分配
}
}
然后,两个makeN
函数都将返回一个包装非空*Foo
值的any
:
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
进一步考虑:
- 在你的第一个尝试中,
return makeNewA[T]
是不起作用的,因为条件reflect.TypeOf(v).Kind() == reflect.Ptr
是在运行时评估的,而makeNewA
的实例化是在编译时发生的。在编译时,T
只是受到any
的约束,而any
(即interface{}
)并不实现Ptr[U]
。 - 你无法仅通过参数
v
捕获关于指针类型和基本类型的信息。例如,当调用makeNew(Foo{})
时,makeNew[T Ptr[U], U any](v T)
将无法编译,而调用makeNew[T Ptr[U], U any](v U)
时,将推断T
为**Foo
。
英文:
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.Ptr
is evaluated at runtime, whereas instantiation ofmakeNewA
happens at compile-time. At compile-timeT
is simply constrained byany
andany
(=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 inferT
as**Foo
when called with*Foo
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论