3

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
4
  • EnumType has to have a return type of []int. Commented Sep 29, 2022 at 1:11
  • Also, you can use doneCar in place of len(CarTypes()) - 1 and fruitDone in place of len(FruitTypes()) - 1 Commented Sep 29, 2022 at 1:13
  • I've tried tackling this another way, and just passing in the convert function, but that doesn't seem to work either. See the update. Commented Sep 29, 2022 at 2:56
  • Your ConvertEnum function has an invalid type parameter. You cannot guarantee a conversion between a int and any. Instead, your type parameter should be ~int to signify that you'll accept an integer or anything that derives from an integer. Commented Sep 29, 2022 at 3:59

1 Answer 1

3

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]},
}
Sign up to request clarification or add additional context in comments.

5 Comments

The latter is PERFECT. Exactly what I needed. Thanks!
No problem at all.
Oh wait - it's close, but then once i have the []int, can i give the function a type to use for casting back (once inside the function)? That is - once I'm in the main function, I'd like to using something to cast the int BACK to the original type - so I can use string on it. (I already have this function elsewhere, that's why i need to cast back) E.g. Fruit(1).String() => "Apple"
Yeah, that's not too hard.
Thanks so much - lemme post where I am in the main body. I think I'm still messing something up here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.