英文:
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("foo", 4.5) // T is inferred from "foo", 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]{} -> must supply T
type Matrix[T any, U ~[]T] []U
// m := Matrix[int, []int]{} -> 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 "fmt"
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("unimplemented")
}
type Ops[T any] interface {
Get() T
}
var _ Ops[any] = new(Queue[any])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论