英文:
Unable to assign untyped int to generic struct field
问题
给定一个通用的结构体:
type R2[IDTYPE comparable] struct {
ID IDTYPE
IsActive bool
}
实现一个接口:
type Storable interface {
Store(ctx context.Context) error
}
我期望以下定义可以工作:
func (r R2[int]) Store(ctx context.Context) error {
r.ID = 123 // 不允许
// ...
return nil
}
然而,这种方法定义是不允许的。错误信息是:
'123' (类型为未指定的 int) 无法用类型 IDTYPE (int) 表示
在 Go Playground 上的错误信息是:
cannot use 123 (未指定的 int 常量) 作为赋值中的 int 值
将其转换为 int(123)
也不起作用。在这种情况下的错误信息是:
cannot use comparable(123) (未指定的 int 常量 123) 作为赋值中的 int 值
英文:
Given a generic struct:
type R2[IDTYPE comparable] struct {
ID IDTYPE
IsActive bool
}
Implementing an interface:
type Storable interface {
Store(ctx context.Context) error
}
I would expect the following definition to work:
func (r R2[int]) Store(ctx context.Context) error {
r.ID = 123 // not allowed
// ...
return nil
}
However, the method definition is not allowed. The error is:
'123' (type untyped int) cannot be represented by the type IDTYPE (int)
Is it not yet possible to do this kind of generic field assignment in Go?
Addendum:
On go playground the error is:
cannot use 123 (untyped int constant) as int value in assignment
And converting to int(123)
does not work. The error in this case is:
cannot use comparable(123) (untyped int constant 123) as int value in assignment
答案1
得分: 1
实例化必须在类型级别上进行,而不是在方法级别上进行,方法不能引入新的类型参数,请参阅https://stackoverflow.com/questions/70668236/how-to-create-generic-method-in-go-method-must-have-no-type-parameters/70668559#70668559
这意味着当您想要使用R2
时,您必须为类型参数选择类型参数,并且方法不能更改这些类型,您在R2
的实例化中选择的类型将会被"固定"。
还要注意,由于IDTYPE
的约束是comparable
,例如可能是string
,整数123
不能在所有情况下分配给ID
字段,因为它可能具有string
类型。
如果您想要/必须处理多个具体类型的ID,则泛型不是正确的选择。可以使用接口代替:
type R2 struct {
ID any
IsActive bool
}
还要注意,如果您希望修改接收器(例如结构体的字段),则接收器必须是指针。
如果您希望将存储在ID
中的值限制为comparable
,请使用(泛型)函数。
以下是如何实现的示例:
type R2 struct {
ID any
IsActive bool
}
func (r *R2) Store(ctx context.Context) error {
setID(r, 123)
return nil
}
func setID[ID comparable](r *R2, id ID) {
r.ID = id
}
进行测试:
r := &R2{}
var s Storable = r
s.Store(context.TODO())
fmt.Println(r)
输出结果(在Go Playground上尝试):
&{123 false}
这提供了灵活性(您可以使用setID()
设置任何可比较的值到ID
字段),并提供了编译时的安全性:尝试设置一个不可比较的值将导致编译时错误,例如:
setID(r, []int{1}) // 错误:[]int不实现comparable
英文:
Instantiation must happen at the type level, not on a method level, and methods can't introduce new type parameters, see https://stackoverflow.com/questions/70668236/how-to-create-generic-method-in-go-method-must-have-no-type-parameters/70668559#70668559
This means when you want to use R2
, you then have to choose type arguments for the type parameters, and the methods can't change those, you're "stuck" with the types you choose on R2
's instantiation.
Also note that since the constraint for IDTYPE
is comparable
, which may be string
for example, the integer 123
cannot be assigned to the ID
field in all cases because it may have a type of string
.
If you want / must handle multiple concrete types for the IDs, generics is not the right choice. Interfaces may be used instead:
type R2 struct {
ID any
IsActive bool
}
Also note that the receiver must be a pointer if you wish to modify the receiver (e.g. fields of a struct).
If you wish to restrict the values stored in ID
to comparable
, use a (generic) function for it.
Here's how you can do it:
type R2 struct {
ID any
IsActive bool
}
func (r *R2) Store(ctx context.Context) error {
setID(r, 123)
return nil
}
func setID[ID comparable](r *R2, id ID) {
r.ID = id
}
Testing it:
r := &R2{}
var s Storable = r
s.Store(context.TODO())
fmt.Println(r)
Which outputs (try it on the Go Playground):
&{123 false}
This provides flexibility (you can set any comparable values to the ID
field using setID()
), and provides compile-time safety: attempting to set an incomparable value will result in a compile-time error such as this:
setID(r, []int{1}) // Error: []int does not implement comparable
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论