在Go语言中,是否有一种方法可以使用两层泛型?

huangapple go评论171阅读模式
英文:

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 &quot;fmt&quot;

type T1 struct{}

func (t T1) F() {
	fmt.Println(&quot;T1&quot;)
}

type T2 struct{}

func (t T2) F() {
	fmt.Println(&quot;T2&quot;)
}

type T interface {
	T1 | T2
}

func Use[S T](t S) {
	t.F() // &lt;== 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(&quot;t does not implement the interface I&quot;)
}

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.

huangapple
  • 本文由 发表于 2023年7月15日 05:41:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76691260.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定