英文:
Using a Pointer to a Function for multiple types in GoLang
问题
我有很多(20+)以下形式的“枚举类型”。我想做的是在一个结构体中按名称列出所有的枚举类型,并且有一个指向返回所有类型的函数的指针。下面是一个简化的示例。
package main
type Fruit int
const (
unknownFruit Fruit = iota // 必须是第一个
Apple
Banana
fruitDone
)
func FruitTypes() []Fruit {
var res []Fruit
for typ := unknownFruit + 1; typ < fruitDone; typ++ {
res = append(res, typ)
}
return res
}
type Car int
const (
unknownCar Car = iota // 必须是第一个
BMW
Mercedes
doneCar
)
func CarTypes() []Car {
var res []Car
for typ := unknownCar + 1; typ < doneCar; typ++ {
res = append(res, typ)
}
return res
}
func main() {
enumTypes := []struct {
Name string
Length int
ConvertFunc func(int) string
}{
{Name: "Fruit", Length: int(fruitDone), ConvertFunc: ConvertEnum[Fruit]},
{Name: "Car", Length: int(doneCar), ConvertFunc: ConvertEnum[Car]},
}
for _, enumType := range enumTypes {
fmt.Println(enumType.Name)
for i := 0; i < enumType.Length; i++ {
fmt.Printf(" -- %s", enumType.ConvertFunc(i))
}
}
}
func ConvertEnum[T any](raw int) string {
return fmt.Sprintf("%v", T(raw)) // #2 - This will not convert
}
不幸的是,我似乎无法正确声明函数指针。正确的返回类型是什么?
(另外,是否有一种方法可以为这些枚举类型声明一个接口和/或使用泛型,以便我不必拥有FruitType()
、CarType()
、PetType()
等等?
=================
更新 - 感谢@Woody1193,我觉得我离成功更近了。我现在使用了枚举是连续数字的事实,所以我实际上不需要传入除了类型之外的任何细节,这样我就可以在循环内部将其转换回来(参见上面的标记#1)。
然而,在上面的代码中,我得到了以下错误:
./main.go:55:64: cannot use EnumType[Fruit] (value of type func(done Fruit) []Fruit) as type func() []int in struct literal
./main.go:56:60: cannot use EnumType[Car] (value of type func(done Car) []Car) as type func() []int in struct literal
./main.go:62:43: too many arguments in call to typ.TypeFunction
=================
更新2 - 我尝试采用不同的角度,只传入转换函数,以将其从结构体中移出(参见上面的代码)。但现在它不允许我进行类型转换
./main.go:68:31: cannot convert raw (variable of type int) to type T
英文:
I have a lot (20+) "Enum Types" of the following form. What I'd like to do is list all the Enum types by name in a struct (see below) and a pointer to a function that returns all of the types. See below for an abbreviated sample.
package main
type Fruit int
const (
unknownFruit Fruit = iota // must be first
Apple
Banana
fruitDone
)
func FruitTypes() []Fruit {
var res []Fruit
for typ := unknownFruit + 1; typ < fruitDone; typ++ {
res = append(res, typ)
}
return res
}
type Car int
const (
unknownCar Car = iota // must be first
BMW
Mercedes
doneCar
)
func CarTypes() []Car {
var res []Car
for typ := unknownCar + 1; typ < doneCar; typ++ {
res = append(res, typ)
}
return res
}
func main() {
enumTypes := []struct {
Name string
Length int
ConvertFunc func(int) string
}{
{Name: "Fruit", Length: int(doneFruit), ConvertFunc: ConvertEnum[Fruit]},
{Name: "Car", Length: int(doneCar), ConvertFunc: ConvertEnum[Car]},
}
for _, enumType := range enumTypes {
fmt.Println(enumType.Name)
for i := 0; i < enumType.Length; i++ {
fmt.Printf(" -- %s", enumType.ConvertFunc(i))
}
}
}
func ConvertEnum[T any](raw int) string {
return fmt.Sprintf("%v", T(raw)) // #2 - This will not convert
}
Unfortunately, I cannot seem to declare the function pointer properly. What's the correct return type?
(As an aside, is there a way to declare an interface for these enum types and/or use generics so that I don't have to have FruitType(), CarType(), PetType()...
=================
UPDATE - thanks to @Woody1193, I feel like I'm much closer. I'm now using the fact that enums are sequential numbers, so I don't really need to pass in any details other than the type so I can cast it back inside the loop (see marker #1 above).
However, in the above, I get:
./main.go:55:64: cannot use EnumType[Fruit] (value of type func(done Fruit) []Fruit) as type func() []int in struct literal
./main.go:56:60: cannot use EnumType[Car] (value of type func(done Car) []Car) as type func() []int in struct literal
./main.go:62:43: too many arguments in call to typ.TypeFunction
=================
Update #2 - I've tried to take a different angle, and just pass in the conversion function, to get it out of the struct. (See above). But now it won't let me cast it
./main.go:68:31: cannot convert raw (variable of type int) to type T
答案1
得分: 3
你在这里遇到的问题是Car
和Fruit
都不是整数,所以TypesFunction
不会接受CarTypes
或FruitTypes
。在这种情况下,泛型是不起作用的,因为声明这样的对象切片需要它们都具有相同的类型参数,这意味着你的代码仍然无法工作。
因此,你最好的选择是修改CarTypes
和FruitTypes
,使它们具有相同的签名func() []int
:
func FruitTypes() []int {
var res []int
for typ := unknownFruit + 1; typ < fruitDone; typ++ {
res = append(res, typ)
}
return res
}
func CarTypes() []int {
var res []int
for typ := unknownCar + 1; typ < doneCar; typ++ {
res = append(res, typ)
}
return res
}
当然,这意味着当你想要特定类型的枚举时,你将需要使用类型转换,但代码将能够工作。
另外,你可以使用泛型来生成函数本身:
func EnumType[T ~int](done T) []int {
var res []int
for typ := T(1); typ < done; typ++ {
res = append(res, typ)
}
return res
}
func ConvertEnum[T ~int](raw ...int) []T {
res := make([]T, len(raw))
for i, typ := range raw {
res[i] = T(typ)
}
return res
}
enumTypes := []struct {
Name string
TypesFunction func() []int
}{
{Name: "Fruit", TypesFunction: EnumType[Fruit]},
{Name: "Car", TypesFunction: EnumType[Car]},
}
英文:
The issue you're having here is that neither Car
nor Fruit
is actually an integer, so TypesFunction
will not accept CarTypes
or FruitTypes
. Generics will not work in this situation because declaring a slice of such objects would require them to all have the same type parameter, which means your code still wouldn't work.
Therefore, your best option is to modify CarTypes
and FruitTypes
so that they have the same signature, func() []int
:
func FruitTypes() []int {
var res []int
for typ := unknownFruit + 1; typ < fruitDone; typ++ {
res = append(res, typ)
}
return res
}
func CarTypes() []int {
var res []int
for typ := unknownCar + 1; typ < doneCar; typ++ {
res = append(res, typ)
}
return res
}
Of course, this will mean that you'll have to use type casting when you want the specific type of enum, but the code will work.
As an aside, you can use generics to generate the function itself:
func EnumType[T ~int](done T) []int {
var res []int
for typ := T(1); typ < done; typ++ {
res = append(res, typ)
}
return res
}
func ConvertEnum[T ~int](raw ...int) []T {
res := make([]T, len(raw))
for i, typ := range raw {
res[i] = T(typ)
}
return res
}
enumTypes := []struct {
Name string
TypesFunction func() []int
}{
{Name: "Fruit", TypesFunction: EnumType[Fruit]},
{Name: "Car", TypesFunction: EnumType[Car]},
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论