Go错误:无法在未实例化的情况下使用通用类型。

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

Go error: cannot use generic type without instantiation

问题

学习Go泛型时,我遇到了一个似乎无法解决的错误。我已经将代码简化为最简单的形式:

type opStack[T any] []T

func main() {

    t := make(opStack)
    // t := new(opStack)
    t = append(t, 0)
    fmt.Println(t[0])
}

在playground中,这个代码在make()调用处(以及被注释掉的new调用处)报错,错误信息如下:

无法使用未实例化的泛型类型opStack[T any]

但是,make()是一个实例化函数。所以,我猜测我可能遗漏了一些语法细节。Go在抱怨什么,需要做什么样的修正呢?

英文:

Studying Go generics, I'm running into an error I can't seem to untangle. I've boiled it down to the simplest code:

type opStack[T any] []T

func main() {

	t := make(opStack)
	//	t := new(opStack)
	t = append(t, 0)
	fmt.Println(t[0])
}

In playground, this bonks at the make() call (and similarly on the new call that's commented out) with the following error message:
> cannot use generic type opStack[T any] without instantiation

But make() is an instantiating function. So, I expect I'm missing some syntactical subtlety. What is Go complaining about and what's the needed correction?

答案1

得分: 28

每当你使用一个参数化类型时,包括在内置的make函数中需要类型参数的任何地方,你都必须用实际类型替换其定义中的类型参数。这被称为实例化。

t := make(opStack[int], 0)
t = append(t, 0)

如果你将一个泛型类型用作另一个泛型类型的类型参数,也必须进行实例化:

type Data[T any] struct {
    data T
}

d := Data[opStack[int]]{ data: []int{0, 1, 2} }

你可以在函数签名、字段和类型定义中使用类型参数进行实例化:

type FooBar[T any] struct {
    ops opStack[T]
}

type OpsMap[T any] map[string]opStack[T]

func echo[T any](ops opStack[T]) opStack[T] { return ops }

语言规范中相关的引用(目前)分别位于两个不同的位置,类型定义

> 如果类型定义指定了类型参数,类型名称表示一个泛型类型。泛型类型在使用时必须进行实例化

实例化

> 通过用类型参数替换类型参数来实例化泛型函数或类型。[...]

在其他编程语言中,“实例化”可能指的是创建对象的实例 - 在Go中,该术语特指用具体类型替换类型参数。在我看来,这个术语的用法仍然是一致的,尽管在Go中它不一定意味着分配内存。

<hr>

请注意,你可以在不指定显式类型参数的情况下调用泛型函数。实例化也会在这里发生,只是类型参数可能都从函数参数中推断出来:

func Print[T, U any](v T, w U) { /* ... */ }

Print("foo", 4.5) // T从"foo"中推断出来,U从4.5中推断出来

推断曾经也适用于泛型类型,但有一个限制,即类型参数列表必须非空。然而,这个特性已被禁用,所以你必须显式地提供所有类型参数。

type Vector[T any] []T 
// v := Vector[int]{} -> 必须提供T

type Matrix[T any, U ~[]T] []U 
// m := Matrix[int, []int]{} -> 必须提供T和U
英文:

Whenever you use a parametrized type, including anywhere a type argument is required, like in the built-in make, you must replace the type parameters in its definition with actual types. This is called instantiation.

t := make(opStack[int], 0)
t = append(t, 0)

A generic type must be instantiated also if you use it as a type argument to another generic type:

type Data[T any] struct {
    data T
}

d := Data[opStack[int]]{ data: []int{0, 1, 2} }

You can instantiate with a type parameter, for example in function signatures, fields and type definitions:

type FooBar[T any] struct {
    ops opStack[T]
}

type OpsMap[T any] map[string]opStack[T]

func echo[T any](ops opStack[T]) opStack[T] { return ops }

The relevant quotes from the language specs are (currently) in two different places, Type definitions:

> If the type definition specifies type parameters, the type name denotes a generic type. Generic types must be instantiated when they are used.

and Instantiations

> A generic function or type is instantiated by substituting type arguments for the type parameters. [...]

In other programming languages, "instantiation" may refer to creating an instance of an object — in Go the term specifically refers to replacing type params with concrete types. In my view, the usage of the term is still consistent, although in Go it doesn't necessarily imply allocation.

<hr>

Note that you may call generic functions without explicit type arguments. Instantiation happens there too, simply the type arguments might all be inferred from the function arguments:

func Print[T, U any](v T, w U) { /* ... */ }

Print(&quot;foo&quot;, 4.5) // T is inferred from &quot;foo&quot;, U from 4.5

Inference used to work also in generic types, with the restriction that the type parameter list had to be non-empty. However this feature has been disabled, so you must supply all type params explicitly.

type Vector[T any] []T 
// v := Vector[int]{} -&gt; must supply T

type Matrix[T any, U ~[]T] []U 
// m := Matrix[int, []int]{} -&gt; must supply T and U

答案2

得分: 3

因为你想要执行 t = append(t, 0) 这行代码,所以数据类型可以是 int 或 float 组。

这段代码应该可以工作:

package main

import "fmt"

func main() {
	type opStack[T any] []T

	t := make(opStack[int], 0) // 在这里你必须初始化数据类型
	t = append(t, 0)
	fmt.Println(t[0])
}
英文:

because you want

> t = append(t, 0)

the data type can be int or float group.

this code should work

package main

import &quot;fmt&quot;

func main() {
	type opStack[T any] []T

	t := make(opStack[int], 0) // You must initialize data type here
	t = append(t, 0)
	fmt.Println(t[0])
}

答案3

得分: 0

感谢您对此的详细解释。我正在尝试找到一个相关问题的答案,即如何在泛型中使用struct typing。我找到了一种方法,认为它可能对类似的情况有帮助。

package main

type Queue[T any] struct {
}

// Get implements Ops.
func (*Queue[T]) Get() T {
	panic("unimplemented")
}

type Ops[T any] interface {
	Get() T
}

var _ Ops[any] = new(Queue[any])
英文:

Thanks for detailed explanation on it. I was trying to find the answer for a related question on how to do struct typing with generics https://stackoverflow.com/questions/76386802/go-structural-typing-with-generics. I found the way to do it thought it might be helpful in similar cases.

package main

type Queue[T any] struct {
}

// Get implements Ops.
func (*Queue[T]) Get() T {
	panic(&quot;unimplemented&quot;)
}

type Ops[T any] interface {
	Get() T
}

var _ Ops[any] = new(Queue[any])

huangapple
  • 本文由 发表于 2022年2月26日 13:42:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/71274361.html
匿名

发表评论

匿名网友

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

确定