Go泛型:无效的复合字面量

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

Go generics: Invalid composite literal

问题

以下是代码的翻译结果:

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

如果我只在接口thing中包含foo,或者移除a stringb int,使得foobar完全相同,那么代码将可以正常运行。然而,这不是泛型的初衷吗?为什么我不能像这样实例化一个泛型类型,尤其是当我甚至没有访问任何字段时?

可能与https://github.com/golang/go/issues/48522有关。

英文:

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

答案1

得分: 2

大多数通用类型不适用于复合字面量。不过,这并不是个问题,因为还有其他方法可以创建通用类型的值。

要创建一个指向新的零值的指针:

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

或者创建一个非指针的零值:

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

至于为什么会以这种方式出现错误,以下是规范中对此的解释,经过修改以回答你的具体问题。

对于复合字面量
> 字面类型的核心类型 T 必须是结构体、数组、切片或映射类型。

什么是核心类型

> 如果接口 T 有一个核心类型,那么[...]存在一个单一类型 U,它是 T 的类型集中所有类型的底层类型。
>
> 没有其他接口有核心类型。

什么是底层类型
> 每个类型 T 都有一个底层类型:如果 T 是预声明的布尔、数值或字符串类型,或者是一个类型字面量,那么相应的底层类型就是 T 本身。否则,T 的底层类型是 T 在其声明中引用的类型的底层类型。

"类型字面量"可以指代字面结构体类型,比如 struct{int id}。因此,当 foobar 都具有 底层类型 struct{int id} 时,thing 具有 核心类型 struct{int id},因此可以使用复合字面量。当 foobar 没有相同的底层类型时,thing 没有核心类型,因此无法使用复合字面量,这就是出现错误的原因。

正式定义可能看起来很复杂,但结果和实际要点很简单:通用代码只能表达可能类型之间的共同行为。除非所有底层类型都相同,否则字面值不是一种常见行为。

英文:

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.

huangapple
  • 本文由 发表于 2023年1月8日 10:35:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75045168.html
匿名

发表评论

匿名网友

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

确定