英文:
Instantiation failure caused by function value with approximate constraint
问题
对于上面的代码,如果我像这样实例化它,运行时会出错(无法推断出 S):
b := GAddAll[int]
fmt.Printf("%v", b(3, []int{1, 2}))
但是像这样使用它就可以正常工作:
fmt.Printf("%v", GAddAll[int](3, []int{1, 2}))
我想知道为什么。
英文:
func GAddAll[E int, S ~[]E](e E, s S) S {
copyS := make(S, len(s))
for i, v := range s {
copyS[i] = v + e
}
return copyS
}
For the above code, if I instantiate it like this, it will give an error when running(cannot infer S)
b := GAddAll[int]
fmt.Printf("%v", b(3, []int{1, 2}))
But it works fine like this
fmt.Printf("%v", GAddAll[int](3, []int{1, 2}))
I want to know why.
答案1
得分: 2
实例化失败是因为S ~[]E
具有近似约束,并且没有足够的类型信息来实例化S
。
当你使用以下代码给函数赋值时:
b := GAddAll[int]
函数已经被实例化。引用规范:
> 一个未被调用的泛型函数需要一个类型参数列表来进行实例化;如果列表是部分的,那么所有剩余的类型参数必须是可推断的。
然而,S
是不可推断的。编译器只能获得的信息是类型参数E
。在第一次替换后,编译器只能推断出约束~[]E
,现在是~[]int
,但类型参数S
仍然未知。
由于带有波浪符(~
)的近似类型集是几乎无限的,无法确定S
是什么——它可以是type FooSlice []int
,因此实例化失败。
因此,如果你需要传递函数值,必须通过提供两个类型参数来进行实例化:
b := GAddAll[int, []int] // ok
fmt.Printf("%v", b(3, []int{1, 2}))
而对于调用表达式:
GAddAll[int](3, []int{1, 2})
编译器仍然无法从类型参数int
推断出S
,但它可以从非类型参数[]int{1,2}
推断出S
,然后实例化函数。
> 一个被调用的泛型函数可以提供(可能是部分的)类型参数列表,或者可以完全省略它,如果省略的类型参数可以从普通(非类型)函数参数中推断出来。
作为澄清,如果你从S ~[]E
中移除波浪符,像这样:
func GAddAll[E int, S []E](e E, s S) S {}
推断将成功,只需提供E
,因为S
的约束的类型集的基数将为1(一个确切的类型)。
英文:
Instantiation fails because S ~[]E
has an approximate constraint, and there isn't enough type information to instantiate S
.
When you assign the function value with:
b := GAddAll[int]
the function is already being instantiated. Quoting the spec:
> A generic function that is is not called requires a type argument list for instantiation; if the list is partial, all remaining type arguments must be inferrable.
However S
isn't inferrable. The only information available to the compiler is the type parameter E
. After a first pass of substitution, the compiler is only able to infer the constraint ~[]E
, which is now ~[]int
, but the type argument S
is still unknown.
Given that approximate type sets with tilde (~
) are virtually infinite, there's no way to conclusively determine what S
is — it could be type FooSlice []int
— and instantiation fails.
So if you need to pass around the function value, you must instantiate by supplying both type parameters:
b := GAddAll[int, []int] // ok
fmt.Printf("%v", b(3, []int{1, 2}))
Instead with the call expression:
GAddAll[int](3, []int{1, 2})
the compiler still can't infer S
from the type argument int
, but it can infer it from the non-type argument []int{1,2}
, and then instantiate the function.
> A generic function that is called may provide a (possibly partial) type argument list, or may omit it entirely if the omitted type arguments are inferrable from the ordinary (non-type) function arguments.
As a clarification, if you were to remove the tilde from the S ~[]E
, like this:
func GAddAll[E int, S []E](e E, s S) S {}
inference would succeed by supplying only E
, because the type set of S
's constraint would have cardinality 1 (an exact type).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论