英文:
Create new object of typed value via Go (go 1.18) generics
问题
我正在使用Go 1.18的beta版本中的泛型进行实验。下面的示例中的Create
函数应该创建一个新的*T
实例(也就是*Apple
)。我尝试使用反射包来实现,但没有成功。
请问你能否告诉我如何修改下面示例中的Create
函数,以便它创建T
的实例而不是返回nil并导致示例崩溃?
type FruitFactory[T any] struct{}
func (f FruitFactory[T]) Create() *T {
//在这里如何创建非nil的水果?
return nil
}
type Apple struct {
color string
}
func example() {
appleFactory := FruitFactory[Apple]{}
apple := appleFactory.Create()
//因为空指针访问而导致恐慌
apple.color = "red"
}
英文:
I am playing with generics in beta release of go 1.18. Create function in example below should create new instance of *T
(therefore *Apple
). I tried to use reflect package for that, but without luck.
Can you please show me how I can change function Create
from the example below so that it creates instance of T
instead of returning nil and crashing my example?
type FruitFactory[T any] struct{}
func (f FruitFactory[T]) Create() *T {
//how to create non-nil fruit here?
return nil
}
type Apple struct {
color string
}
func example() {
appleFactory := FruitFactory[Apple]{}
apple := appleFactory.Create()
//panics because nil pointer access
apple.color = "red"
}
答案1
得分: 18
由于您正在使用非指针类型(Apple
)实例化FruitFactory
,您可以只声明一个带有类型的变量并返回其地址:
func (f FruitFactory[T]) Create() *T {
var a T
return &a
}
或者:
func (f FruitFactory[T]) Create() *T {
return new(T)
}
Playground: https://gotipplay.golang.org/p/IJErmO1mrJh
如果您想要使用指针类型实例化FruitFactory
并且仍然避免段错误,情况会变得更加复杂。基本上,您必须在方法体中声明一个非指针类型的变量,并将其转换为指针类型。
// 将类型约束为其指针类型
type Ptr[T any] interface {
*T
}
// 第一个类型参数将匹配指针类型并推断出U
type FruitFactory[T Ptr[U], U any] struct{}
func (f FruitFactory[T, U]) Create() T {
// 声明非指针类型的变量。这不是nil!
var a U
// 取其地址并转换为指针类型(仍然不是nil)
return T(&a)
}
type Apple struct {
color string
}
func main() {
// 使用指针类型进行实例化
appleFactory := FruitFactory[*Apple, Apple]{}
apple := appleFactory.Create()
// 一切正常
apple.color = "red"
fmt.Println(apple) // &{red}
}
Playground: https://gotipplay.golang.org/p/07nUGI-xP0O
编辑,2022年3月:已禁用定义类型的类型推断,因此第二个示例代码不再编译。保留原始代码以供参考。您必须提供所有类型参数:FruitFactory[*Apple, Apple]{}
,这使得代码相当冗长。对于函数,类型推断仍然正常工作。
英文:
Since you are instantiating FruitFactory
with a non-pointer type (Apple
), you can just declare a typed variable and return its address:
func (f FruitFactory[T]) Create() *T {
var a T
return &a
}
Or:
func (f FruitFactory[T]) Create() *T {
return new(T)
}
Playground: https://gotipplay.golang.org/p/IJErmO1mrJh
<hr>
If you want to instantiate FruitFactory
with a pointer type and still avoid segmentation faults, things get more complicated. Basically you have to <strike>take advantage of type inference to</strike> declare a variable of the non-pointer type in the method body and convert that to the pointer type.
// constraining a type to its pointer type
type Ptr[T any] interface {
*T
}
// the first type param will match pointer types and infer U
type FruitFactory[T Ptr[U], U any] struct{}
func (f FruitFactory[T,U]) Create() T {
// declare var of non-pointer type. this is not nil!
var a U
// address it and convert to pointer type (still not nil)
return T(&a)
}
type Apple struct {
color string
}
func main() {
// instantiating with ptr type
appleFactory := FruitFactory[*Apple, Apple]{}
apple := appleFactory.Create()
// all good
apple.color = "red"
fmt.Println(apple) // &{red}
}
Playground: https://gotipplay.golang.org/p/07nUGI-xP0O
EDIT, March 2022: type inference for defined types has been disabled, so the second playground doesn't compile anymore. Leaving the original one for reference. You must supply all type parameters: FruitFactory[*Apple, Apple]{}
, which does make it quite verbose. Type inference works normally for functions.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论