通过Go(go 1.18)泛型创建一个新的类型化值对象。

huangapple go评论85阅读模式
英文:

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(&amp;a)
}

type Apple struct {
	color string
}

func main() {
    // instantiating with ptr type
	appleFactory := FruitFactory[*Apple, Apple]{}
	apple := appleFactory.Create()

    // all good
	apple.color = &quot;red&quot;

	fmt.Println(apple) // &amp;{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.

huangapple
  • 本文由 发表于 2021年12月17日 22:40:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/70394814.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定