Ondra
Ondra

Reputation: 3180

Create new object of typed value via Go (go 1.18) generics

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"
}

Upvotes: 12

Views: 13702

Answers (1)

blackgreen
blackgreen

Reputation: 45162

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


If you want to instantiate FruitFactory with a pointer type and still avoid segmentation faults, things get more complicated. Basically you have to take advantage of type inference to 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.

Upvotes: 28

Related Questions