Home > Net >  Go generics: Invalid composite literal
Go generics: Invalid composite literal

Time:01-08

The following code results in the error "invalid composite literal type T".

package main

import "fmt"

func main() {
    fmt.Println(createThing[foo]())
}

type thing interface {
    foo | bar
}

type foo struct {
    id int
    a  string
}

type bar struct {
    id int
    b  int
}

func createThing[T thing, P *T]() P {
    return &T{}
}

If I only include foo in interface thing, or remove a string and b int so foo and bar are exactly the same, the code will run without error. However, doesn't this defeat the purpose of generics? Why can't I instantiate a generic type like this, especially when I'm not even accessing any fields?

Possibly related to https://github.com/golang/go/issues/48522

CodePudding user response:

Most generic types are not valid types for composite literals. This isn't a problem though, as there are other ways to create values of generic types.

To create a pointer to a new zero value:

func createThing[T thing]() *T {
    return new(T)
}

Or to create a non-pointer zero value:

func createThing[T thing]() T {
    var value T
    return value
}

As for why the error occurs in this way, here's the explanation from the spec, revised to address your specific question.

For composite literals:

The LiteralType's core type T must be a struct, array, slice, or map type

What is the core type?

An interface T has a core type if [...] there is a single type U which is the underlying type of all types in the type set of T

No other interfaces have a core type.

What is the underlying type?

Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its declaration.

A "type literal" can refer to a literal struct type, like struct{int id}. So, when foo and bar both have an underlying type of struct{int id}, then thing has a core type of struct{int id}, and so composite literals are possible. When foo and bar don't have the same underlying type, then thing has no core type, and composite literals are not possible, hence your error.

The formal definition may seem complicated, but the result and practical takeaway is simple: generic code is only capable of expressing common behaviour across the possible types. Besides in the special case where all underlying types are the same, literal values are not a common behaviour.

  • Related