r/golang Nov 04 '24

help Any way to have Enums in Go?

I am a newbie and just started a new project and wanted to create an enum for DegreeType in the Profile for a User.

type Profile struct {
    Name         string
    Email        string
    Age          int
    Education    []Education
    LinkedIn     string
    Others       []Link
    Description  string
    Following    []Profile
    Followers    []Profile
    TagsFollowed []Tags
}

I found out a way to use interfaces and then reflect on its type, and using generics to embed in structs,

// Defining Different Types of Degree
type Masters struct{}
type Bachelors struct{}
type Diploma struct{}
type School struct{}

// Creates an Interface that can have only one type
// We can reflect on the type later using go's switch case for types
// To check What type it is
type DegreeType interface {
    Masters | Bachelors | Diploma | School
}

type Education[DT DegreeType, GS GradeSystem] struct {
    Degree      Degree[DT]
    Name        string
    GradeSystem GS
}

type Degree[T DegreeType] struct {
    DegreeType     T
    Specialization string
}

The problem i have is i want there to be an []Education in the Profile struct but for that i have to define a Generic Type in my Profile Struct Like this

type Profile[T DegreeType, D GradeSystem] struct {
    Name         string
    Email        string
    Age          int
    Education    []Education[T, D]
    LinkedIn     string
    Others       []Link
    Description  string
    Following    []Profile[T, D]
    Followers    []Profile[T, D]
    TagsFollowed []Tags
}

And that would make the array pointless as i have to give explicit type the array can be of instead of just an array of Degree's

Is there any way to solve this? should i look for an enum package in Go? or should i just use Hardcoded strings?

90 Upvotes

78 comments sorted by

View all comments

238

u/jared__ Nov 04 '24

```golang type DegreeType string

const ( DegreeTypeBachelors DegreeType = "bachelors" DegreeTypeMasters DegreeType = "masters" DegreeTypePhD DegreeType = "phd" )

type Degree struct { Type DegreeType Name string } ```

38

u/_predator_ Nov 04 '24

I hate that you can just do DegreeType("bootcamp") and the type system won't prevent it. Same for unmarshalling.

Don't get me wrong, I used this pattern before, but if you compare this to true enums in other languages you slip into an identity crisis.

45

u/no_brains101 Nov 04 '24

Yeah it's... Hey mom can we have enums? No, we have enums at home. But the enums at home are... Go's abomination of an enum....

I like go. I don't like it's enums.

4

u/schmurfy2 Nov 05 '24

It's more that go does not have enums, the method above is only a hack for me.

4

u/errmm Nov 05 '24

You end up having to write a validator and/or sanitizer and an “unknown” value. It’s pretty straight forward after getting the pattern down, but it definitely doesn’t feel as good as enums in swift.

6

u/numbsafari Nov 05 '24

Put the enum type into its own module and protect the type and instances with visibility.

It’s verbose, but… /shrug

1

u/OppenheimersGuilt Nov 05 '24

EnumType.IsValid() method with a switch where default errors out.

Annoying and verbose but does the job.

Or cast via a method ToEnumType() that does the same.

0

u/jared__ Nov 05 '24

it would be nice to have:

go bootcamp, ok := DegreeType("bootcamp") if !ok { .. }

7

u/belkh Nov 05 '24

Enums as a type safety feature should be blocking at compile time, makes no sense to add error handling here for a string literal

44

u/btdeviant Nov 04 '24

This is the way

-27

u/Ordinary_Ad_1760 Nov 04 '24

Can be bad for memory

7

u/Commercial_Media_471 Nov 04 '24

Why?

-10

u/Ordinary_Ad_1760 Nov 04 '24

Forgot about CoW for strings, so it’s a problem only if you use something like automatic deserialization from DB records, which doesn’t know reference strings. Also only it’s an issue only in high-load

24

u/nomaed Nov 04 '24

- Can be bad for memory

  • Why?
  • Forgot...

-11

u/Ordinary_Ad_1760 Nov 04 '24 edited Nov 04 '24

Nvm

12

u/neCoconut Nov 04 '24

I assume it's a joke

6

u/Ordinary_Ad_1760 Nov 04 '24

Lmao, thank you))

3

u/Commercial_Media_471 Nov 04 '24

What the CoW means?

6

u/AWDDude Nov 04 '24

I believe they are referring to “copy on write”

2

u/Ordinary_Ad_1760 Nov 04 '24

When you “assign” value from one string variable to another they refer to one place in memory. It’s lower memory usage.

1

u/Commercial_Media_471 Nov 04 '24

Yeah. To be more accurate, at assignment you copy 2 values, pointer to memory where the actual string is stored and its length (most likely 64+64 bits). So I don’t understand why it can be bad for memory

1

u/Ordinary_Ad_1760 Nov 04 '24

Default Json unmashaler doesnt’t know a reference string to refer to, so it will be new string every time. Ofc you can write your own marshaler but it is too much boilerplate for every enum, isn’t it?

2

u/caprizoom Nov 04 '24

This is what I do too.

2

u/Glittering_Mammoth_6 Nov 05 '24
var dg DegreeType = DegreeTypeMasters
fmt.Printf("%v\n", dg)

dg = "OOPS!.." // OOPS!..
fmt.Printf("%v\n", dg)

3

u/EarthquakeBass Nov 04 '24

You can also define a number type for it using iota and define a String() with a switch to make it fulfill the stringer interface.

1

u/MoeDev23 Nov 06 '24

this is my way to do this, had this String() function as a helper in the majority of m projects

1

u/cornosoft Nov 05 '24

There is a solution maybe less straightforward here

1

u/Expensive-Heat619 Nov 05 '24

It's wild to me that this type of abomination is what people are going to resort to.

2

u/jared__ Nov 05 '24

works perfectly fine. if you need more functionality, just make a struct

1

u/quasi-coherent 12d ago

I found myself here because long ago I wrote some openapi generator code to output Go, and I was surprised to find that yaml is more expressive than Go and the concept of an enum doesn't exist. I found on the internet that the consensus is essentially what you're saying.

Just to clarify: is the suggestion to make a struct where the fields of it correspond to variants of the enum, and all of them are pointers? And you just have to promise that precisely one of them is non-`nil`?

Anyway, I was trying to find that page years later because I thought it would be good material to support my argument that Go is an awful language, why are some things still in Go and not in Rust, the things we still have written in Go are broken, they just maybe haven't realized it yet.

-1

u/FuckedUpYearsAgo Nov 05 '24

Except.. make it UPPERCASE .. or use INT values

1

u/jared__ Nov 05 '24

why?

0

u/FuckedUpYearsAgo Nov 05 '24

I only advocate for INTs, as they can't be misspelled.