aronchick
aronchick

Reputation: 7128

Using a Pointer to a Function for multiple types in GoLang

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

Upvotes: 3

Views: 339

Answers (1)

Woody1193
Woody1193

Reputation: 8010

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]},
}

Upvotes: 3

Related Questions