英文:
Is there a way to use two levels of generics in Go?
问题
我正在为遗传算法编写一个库,并想知道是否有办法使用两层泛型。类的结构是这样的:type Gene[T comparable] struct
包含类型为 T
的基因;type Allele[T comparable] struct
包含 Gene[T]
;type Chromosome[T comparable] struct
包含 Allele[T]
;type Genome[T comparable] struct
包含 Chromosome[T]
。我想编写一个优化函数,可以将 type Code[T comparable] interface { Gene[T] | Allele[T] | Chromosome[T] | Genome[T] }
作为参数,例如 func Optimize[T comparable, C Code[T]](params OptimizationParams)
。然而,访问类型为 C
的变量的方法会导致错误 "(type *C is pointer to type parameter, not type parameter)"。如果我将函数签名更改为 func Optimize[T comparable, C *Code[T]](...)
,错误变为 "cannot use type Code[T] outside a type constraint: interface contains type constraints" 和 "gene.Recombine undefined (type *C is pointer to type parameter, not type parameter)"。
我创建了两个简化的示例,展示了这个问题的两种形式。我的解决方案是使用相同的代码四次编写所有函数,只在必要时更改类型,但这有点繁琐,因为我喜欢保持代码的 DRY(Don't Repeat Yourself)。
在Go语言中有办法使用两层泛型吗?或者,使用DRY的解决方案是创建一个包装结构体?
英文:
I am working on a library for genetic algorithms and wanted to know if there was a way to use two levels of generics. The class structure is that type Gene[T comparable] struct
contains bases of type T
; type Allele[T comparable] struct
contains Gene[T]
s; type Chromosome[T comparable] struct
contains Allele[T]
s; and type Genome[T comparable] struct
contains Chromosome[T]
s. I wanted to write an optimization function that could take a type Code[T comparable] interface { Gene[T] | Allele[T] | Chromosome[T] | Genome[T] }
as a parameter, e.g. func Optimize[T comparable, C Code[T]](params OptimizationParams)
. However, accessing the methods on variables of type C
resulted in the error "(type *C is pointer to type parameter, not type parameter)". If I change the function signature to func Optimize[T comparable, C *Code[T]](...)
, the error becomes "cannot use type Code[T] outside a type constraint: interface contains type constraints" and "gene.Recombine undefined (type *C is pointer to type parameter, not type parameter)".
I have created simplified examples that demonstrate this issue in both forms. My solution was to just write all the functions four times using the same code with just the types changed where necessary, which is a bit of a drag since I like to keep my code DRY.
Is there a way to use two levels of generics in Go? Or would the DRY solution be to create a wrapper struct?
答案1
得分: 1
对于这个错误信息,请参考https://stackoverflow.com/questions/71444847/go-with-generics-type-t-is-pointer-to-type-parameter-not-type-parameter。
让我们先解引用指针:
func TopAreas[T comparable, S Shape[T]](shapes []SizedShape[T, S]) [2]float32 {
return [2]float32{(*(shapes[0].shape)).Area(), (*(shapes[1].shape)).Area()}
}
现在的错误是:
(*(shapes[0].shape)).Area undefined (type S has no field or method Area)
让我们用一个简化的示例重现这个问题:
package main
import "fmt"
type T1 struct{}
func (t T1) F() {
fmt.Println("T1")
}
type T2 struct{}
func (t T2) F() {
fmt.Println("T2")
}
type T interface {
T1 | T2
}
func Use[S T](t S) {
t.F() // <== t.F undefined (type T has no field or method F)
}
func main() {
Use(T1{})
Use(T2{})
}
错误是:
t.F undefined (type T has no field or method F)
第一种方法是使用type assertion来检查对象是否具有方法F
,如果有则调用该方法:
func Use[S T](t S) {
type I interface {
F()
}
tt, ok := any(t).(I)
if ok {
tt.F()
}
panic("t does not implement the interface I")
}
这是一个基于你的代码的完整示例,使用类型断言来调用Area
:https://go.dev/play/p/eI-RHw-Z5DQ。
更好的方法是将T
定义为一个基本接口:
type T interface {
F()
}
并且Use
函数可以实现为:
func Use(t T) {
t.F()
}
这是一个基于你的代码的完整示例,将Shape
定义为一个基本接口:https://go.dev/play/p/YQKRLKI_f3Z。
英文:
> type *C is pointer to type parameter, not type parameter
For this error message, see https://stackoverflow.com/questions/71444847/go-with-generics-type-t-is-pointer-to-type-parameter-not-type-parameter.
Let's dereference the pointer first:
func TopAreas[T comparable, S Shape[T]](shapes []SizedShape[T, S]) [2]float32 {
return [2]float32{(*(shapes[0].shape)).Area(), (*(shapes[1].shape)).Area()}
}
Now the error is:
(*(shapes[0].shape)).Area undefined (type S has no field or method Area)
Let's reproduce the issue with a simplified demo:
package main
import "fmt"
type T1 struct{}
func (t T1) F() {
fmt.Println("T1")
}
type T2 struct{}
func (t T2) F() {
fmt.Println("T2")
}
type T interface {
T1 | T2
}
func Use[S T](t S) {
t.F() // <== t.F undefined (type T has no field or method F)
}
func main() {
Use(T1{})
Use(T2{})
}
The error is:
t.F undefined (type T has no field or method F)
The first approach is to use a type assertion to check whether the object has the method F
and call the method if it does:
func Use[S T](t S) {
type I interface {
F()
}
tt, ok := any(t).(I)
if ok {
tt.F()
}
panic("t does not implement the interface I")
}
Here is a full example based on your code that uses type assertion to call Area
: https://go.dev/play/p/eI-RHw-Z5DQ.
A better approach is to define T
as a basic interface:
type T interface {
F()
}
And the Use
func can be implemented as:
func Use(t T) {
t.F()
}
Here is a full example based on your code that defines Shape
as a basic interface: https://go.dev/play/p/YQKRLKI_f3Z.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论