英文:
Assigning a value literal to a struct field of a generic type without running into an IncompatibleAssign error
问题
这是可以的:
type constraint interface {
~float32 | ~float64
}
type foo[T constraint] struct {
val T
}
func (f *foo[float64]) setValToPi() {
f.val = 3.14
}
然而,如果我将constraint
更改为包括int
类型,就会遇到错误:
type constraint interface {
~float32 | ~float64 | ~int
}
type foo[T constraint] struct {
val T
}
func (f *foo[float64]) setValToPi() {
f.val = 3.14 // IncompatibleAssign: cannot use 3.14 (untyped float constant) as float64 value in assignment
}
为什么包含不属于同一“类型组”的类型的约束会导致此错误,我应该如何处理它?
英文:
This is fine:
type constraint interface {
~float32 | ~float64
}
type foo[T constraint] struct {
val T
}
func (f *foo[float64]) setValToPi() {
f.val = 3.14
}
However, if I change constraint
to also include int
types, I run into an error:
type constraint interface {
~float32 | ~float64 | ~int
}
type foo[T constraint] struct {
val T
}
func (f *foo[float64]) setValToPi() {
f.val = 3.14 // IncompatibleAssign: cannot use 3.14 (untyped float constant) as float64 value in assignment
}
Why do constraints that include types that don't belong to the same "type group" cause this error, and how should I deal with it?
答案1
得分: 4
这个语法:
func (f *foo[float64]) setValToPi() {
// ...
}
只是一个方法声明。它不会实例化泛型类型foo
。方括号中的标识符float64
是类型参数的名称。它可以像类型定义中一样是T
。
就好像你写了:
type foo[float64 constraint] struct {
val float64
}
由于float64
是一个预声明的标识符,你可以用类型参数名称来隐藏它。
因此,在方法setValToPi
中,关于val
的类型的唯一已知信息是它被约束为constraint
,例如~float32 | ~float64 | ~int
的并集。
如果你将方法声明更改为:
func (f *foo[T]) setValToPi() {
// ...
}
你将得到相同的错误,只是用T
代替:
cannot use 3.14 (untyped float constant) as T value in assignment
错误是由于3.14
(一个无类型的浮点常量)不能总是分配给所有可能的foo[T]
实例,特别是当T
确实是~int
时。
带有float64
标识符作为类型参数的 Playground:https://gotipplay.golang.org/p/1EuAsSKdihK
一个解决方法是使该方法接受类型参数类型的值(除了使用不那么令人困惑的标识符):
func (f *foo[T]) SetValue(val T) {
f.val = val
}
当然,这意味着你不能设置固定值,比如3.14
,但正如解释的那样,这本身就是一个错误。其他可能的解决方案是使用any
/interface{}
作为字段类型。可以参考这里的一些灵感:这里或这里。
英文:
This syntax:
func (f *foo[float64]) setValToPi() {
// ...
}
Is simply a method declaration. It does not instantiate the generic type foo
. The identifier float64
you have within square brackets is the name of the type parameter. It could be T
, as in the type definition, just as well.
It's like you wrote:
type foo[float64 constraint] struct {
val float64
}
Since float64
is a predeclared identifier, you can shadow it with a type parameter name.
So within the method setValToPi
, the only known information about the type of val
is that it is constrained to constraint
, e.g. the union of ~float32 | ~float64 | ~int
.
If you change the method declaration to:
func (f *foo[T]) setValToPi() {
// ...
}
You will get the same error with T
instead:
> cannot use 3.14 (untyped float constant) as T value in assignment
And the error is given by the fact that 3.14
(an untyped floating-point constant) can't be always assigned to all possible instances of foo[T]
, specifically the one where T
is indeed ~int
.
Playground with the float64
identifier as type param: https://gotipplay.golang.org/p/1EuAsSKdihK
A solution would be to make the method accept a value of type parameter type (beside using less confusing identifiers):
func (f *foo[T]) SetValue(val T) {
f.val = val
}
This of course means you can't set fixed values like 3.14
, but as explained, this is a mistake in the first place. Other possible solutions are using any
/interface{}
as the field type. For some inspiration, see here or here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论