824

I'm trying to represent a simplified chromosome, which consists of N bases, each of which can only be one of {A, C, T, G}.

I'd like to formalize the constraints with an enum, but I'm wondering what the most idiomatic way of emulating an enum is in Go.

5
  • 6
    In go standard packages they're represented as constants. See golang.org/pkg/os/#pkg-constants Commented Jan 20, 2013 at 16:11
  • 2
    related: stackoverflow.com/questions/14236263/… Commented Jan 20, 2013 at 16:12
  • 1
    @carbocation: That's not how duplicates work on Stack Overflow. Questions which are duplicates should be closed as a duplicate of the question with the best content, not the earliest one. Commented Jan 21, 2022 at 11:52
  • 3
    Now that 1.18 is upon us, I wonder if generics have opened up any fresh opportunities for conceptualizing enums in go. Commented May 8, 2022 at 17:04
  • In Go, enums are typically represented using constants with a common prefix, often in the form of const ( EnumValue1 = iota; EnumValue2; ... ) Commented Nov 8, 2024 at 11:13

15 Answers 15

945

Quoting from the language specs:Iota

Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants. It is reset to 0 whenever the reserved word const appears in the source and increments after each ConstSpec. It can be used to construct a set of related constants:

const ( // iota is reset to 0 c0 = iota // c0 == 0 c1 = iota // c1 == 1 c2 = iota // c2 == 2 ) const ( a = 1 << iota // a == 1 (iota has been reset) b = 1 << iota // b == 2 c = 1 << iota // c == 4 ) const ( u = iota * 42 // u == 0 (untyped integer constant) v float64 = iota * 42 // v == 42.0 (float64 constant) w = iota * 42 // w == 84 (untyped integer constant) ) const x = iota // x == 0 (iota has been reset) const y = iota // y == 0 (iota has been reset) 

Within an ExpressionList, the value of each iota is the same because it is only incremented after each ConstSpec:

const ( bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0 bit1, mask1 // bit1 == 2, mask1 == 1 _, _ // skips iota == 2 bit3, mask3 // bit3 == 8, mask3 == 7 ) 

This last example exploits the implicit repetition of the last non-empty expression list.


So your code might be like

const ( A = iota C T G ) 

or

type Base int const ( A Base = iota C T G ) 

if you want bases to be a separate type from int.

Sign up to request clarification or add additional context in comments.

12 Comments

great examples (I did not recall the exact iota behaviour - when it is incremented - from the spec). Personally I like to give a type to an enum, so it can be type-checked when used as argument, field, etc.
Very interesting @jnml . But I'm kind of disappointed that static type-checking seems to be loose, for example nothing prevents me from using Base n°42 which never existed : play.golang.org/p/oH7eiXBxhR
To complement on jnml, even semantically, nothing in the language says that the consts defined as Base represent the whole range of valid Base, it just says that these particular consts are of type Base. More constants could be defined elsewhere as Base too, and it is not even mutually exclusive (e.g. const Z Base = 0 could be defined and would be valid).
You can use iota + 1 to not begin at 0.
Is iota really beneficial construct? Iota most of the time offers sparing like 2 characters per line (ok + whitespaces), what it cost You is: the enum values depends on the order - once it changes in the source (by e.g. CVS merge) You may corrupt existing persisted data by shifting the index, causing altering the meaning of the data. Second, if You have like 15 items in enum and just want quickly to check, what item corresponds to value 11, are You going to count the lines every time? Seriously, we write it once, read it dozens of times.
|
127

Referring to the answer of jnml, you could prevent new instances of Base type by not exporting the Base type at all (i.e. write it lowercase). If needed, you may make an exportable interface that has a method that returns a base type. This interface could be used in functions from the outside that deal with Bases, i.e.

package a type base int const ( A base = iota C T G ) type Baser interface { Base() base } // every base must fulfill the Baser interface func(b base) Base() base { return b } func(b base) OtherMethod() { } 

package main import "a" // func from the outside that handles a.base via a.Baser // since a.base is not exported, only exported bases that are created within package a may be used, like a.A, a.C, a.T. and a.G func HandleBasers(b a.Baser) { base := b.Base() base.OtherMethod() } // func from the outside that returns a.A or a.C, depending of condition func AorC(condition bool) a.Baser { if condition { return a.A } return a.C } 

Inside the main package a.Baser is effectively like an enum now. Only inside the a package you may define new instances.

3 Comments

Your method seems perfect for the cases where base is used only as method receiver. If your a package were to expose a function taking a parameter of type base, then it would become dangerous. Indeed, the user could just call it with the literal value 42, which the function would accept as base since it can be casted to an int. To prevent this, make base a struct: type base struct{value:int}. Problem: you cannot declare bases as constants anymore, only module variables. But 42 will never be cast to a base of that type.
@metakeule I'm trying to understand your example but your choice in variable names has made it exceedingly difficult.
This is one of my bugbears in examples. FGS, I realise it's tempting, but don't name the variable the same as the type!
78

You can make it so:

type MessageType int32 const ( TEXT MessageType = 0 BINARY MessageType = 1 ) 

With this code compiler should check type of enum

7 Comments

Constants are usually written in normal camelcase, not all uppercase. The initial uppercase letter means that variable is exported, which may or may not be what you want.
I've noticed in the Go source code there is a mixture where sometimes constants are all uppercase and sometimes they are camelcase. Do you have a reference to a spec?
@JeremyGailor I think 425nesp is just noting that the normal preference is for developers to use them as unexported constants so use camelcase. If the developer determines that it should be exported then feel free to use all uppercase or capital case because there's no established preference. See Golang Code Review Recommendations and Effective Go Section on Constants
Note that e.g. functions expecting a MessageType will happily accept untyped numeric consts, e.g. 7. Furthermore, You can cast any int32 to MessageType. If you are aware of this, I think this is the most idiomatic way in go.
@MartinM. I'd call that an "accident" -- those constants (KB, MB, GB, etc) just happen to be abbreviations (see github.com/golang/go/wiki/CodeReviewComments#initialisms) to well-known units.
|
74

It's true that the above examples of using const and iota are the most idiomatic ways of representing primitive enums in Go. But what if you're looking for a way to create a more fully-featured enum similar to the type you'd see in another language like Java or Python?

A very simple way to create an object that starts to look and feel like a string enum in Python would be:

package main import ( "fmt" ) var Colors = newColorRegistry() func newColorRegistry() *colorRegistry { return &colorRegistry{ Red: "red", Green: "green", Blue: "blue", } } type colorRegistry struct { Red string Green string Blue string } func main() { fmt.Println(Colors.Red) } 

Suppose you also wanted some utility methods, like Colors.List(), and Colors.Parse("red"). And your colors were more complex and needed to be a struct. Then you might do something a bit like this:

package main import ( "errors" "fmt" ) var Colors = newColorRegistry() type Color struct { StringRepresentation string Hex string } func (c *Color) String() string { return c.StringRepresentation } func newColorRegistry() *colorRegistry { red := &Color{"red", "F00"} green := &Color{"green", "0F0"} blue := &Color{"blue", "00F"} return &colorRegistry{ Red: red, Green: green, Blue: blue, colors: []*Color{red, green, blue}, } } type colorRegistry struct { Red *Color Green *Color Blue *Color colors []*Color } func (c *colorRegistry) List() []*Color { return c.colors } func (c *colorRegistry) Parse(s string) (*Color, error) { for _, color := range c.List() { if color.String() == s { return color, nil } } return nil, errors.New("couldn't find it") } func main() { fmt.Printf("%s\n", Colors.List()) } 

At that point, sure it works, but you might not like how you have to repetitively define colors. If at this point you'd like to eliminate that, you could use tags on your struct and do some fancy reflecting to set it up, but hopefully this is enough to cover most people.

2 Comments

Any way to use this with a switch statement, like in Java?
Actually, I got it working using the public Colors in your example, e.g. case Colors.Red: ...
46

There is a way with struct namespace.

The benefit is all enum variables are under a specific namespace to avoid pollution. The issue is that we could only use var not const

type OrderStatusType string var OrderStatus = struct { APPROVED OrderStatusType APPROVAL_PENDING OrderStatusType REJECTED OrderStatusType REVISION_PENDING OrderStatusType }{ APPROVED: "approved", APPROVAL_PENDING: "approval pending", REJECTED: "rejected", REVISION_PENDING: "revision pending", } 

3 Comments

This is syntactically nice, but I'm paranoid about people changing constants. See this example: play.golang.org/p/9D1tMQJVmIc . If namespacing is important, I would lean towards placing them in their own package.
How can I use that as an enum?
I agree with @Grokify, although this looks syntactically pleasing, the use of var here is quite dangerous, as it is prone to change, which defeats the whole point of an enum.
31

For a use case like this, it may be useful to use a string constant so it can be marshaled into a JSON string. In the following example, []Base{A,C,G,T} would get marshaled to ["adenine","cytosine","guanine","thymine"].

type Base string const ( A Base = "adenine" C = "cytosine" G = "guanine" T = "thymine" ) 

When using iota, the values get marshaled into integers. In the following example, []Base{A,C,G,T} would get marshaled to [0,1,2,3].

type Base int const ( A Base = iota C G T ) 

Here's an example comparing both approaches:

https://play.golang.org/p/VvkcWvv-Tvj

Comments

23

As of Go 1.4, the go generate tool has been introduced together with the stringer command that makes your enum easily debuggable and printable.

2 Comments

Do u know is oposite solution. I mean string -> MyType. Since one way solution is far from ideal. Here is sb gist that do what I want - but writing by hand is easy to make mistakes.
Adding a code-sample in the answer would definately help getting your point across more cleanly.
19

I am sure we have a lot of good answers here. But, I just thought of adding the way I have used enumerated types

package main import "fmt" type Enum interface { name() string ordinal() int values() *[]string } type GenderType uint const ( MALE = iota FEMALE ) var genderTypeStrings = []string{ "MALE", "FEMALE", } func (gt GenderType) name() string { return genderTypeStrings[gt] } func (gt GenderType) ordinal() int { return int(gt) } func (gt GenderType) values() *[]string { return &genderTypeStrings } func main() { var ds GenderType = MALE fmt.Printf("The Gender is %s\n", ds.name()) } 

This is by far one of the idiomatic ways we could create Enumerated types and use in Go.

Edit:

Adding another way of using constants to enumerate

package main import ( "fmt" ) const ( // UNSPECIFIED logs nothing UNSPECIFIED Level = iota // 0 : // TRACE logs everything TRACE // 1 // INFO logs Info, Warnings and Errors INFO // 2 // WARNING logs Warning and Errors WARNING // 3 // ERROR just logs Errors ERROR // 4 ) // Level holds the log level. type Level int func SetLogLevel(level Level) { switch level { case TRACE: fmt.Println("trace") return case INFO: fmt.Println("info") return case WARNING: fmt.Println("warning") return case ERROR: fmt.Println("error") return default: fmt.Println("default") return } } func main() { SetLogLevel(INFO) } 

3 Comments

You can declare constants with string values. IMO it is easier to do that if you intend to display them and don't actually need the numeric value.
I don’t get the second example: you can still call SetLogLevel with any int value. The point of using an enum is that you are not able to use any other value.
It is not a good idea to specify your constants with capital letters only for the sake of being constants as it has a totally different meaning in Go. In Go, capital letters are used for access control and managing the visibility of code.
9

Here is an example that will prove useful when there are many enumerations. It uses structures in Golang, and draws upon Object Oriented Principles to tie them all together in a neat little bundle. None of the underlying code will change when a new enumeration is added or deleted. The process is:

  • Define an enumeration structure for enumeration items: EnumItem. It has an integer and string type.
  • Define the enumeration as a list of enumeration items: Enum
  • Build methods for the enumeration. A few have been included:
    • enum.Name(index int): returns the name for the given index.
    • enum.Index(name string): returns the name for the given index.
    • enum.Last(): returns the index and name of the last enumeration
  • Add your enumeration definitions.

Here is some code:

type EnumItem struct { index int name string } type Enum struct { items []EnumItem } func (enum Enum) Name(findIndex int) string { for _, item := range enum.items { if item.index == findIndex { return item.name } } return "ID not found" } func (enum Enum) Index(findName string) int { for idx, item := range enum.items { if findName == item.name { return idx } } return -1 } func (enum Enum) Last() (int, string) { n := len(enum.items) return n - 1, enum.items[n-1].name } var AgentTypes = Enum{[]EnumItem{{0, "StaffMember"}, {1, "Organization"}, {1, "Automated"}}} var AccountTypes = Enum{[]EnumItem{{0, "Basic"}, {1, "Advanced"}}} var FlagTypes = Enum{[]EnumItem{{0, "Custom"}, {1, "System"}}} 

Comments

7

Refactored https://stackoverflow.com/a/17989915/863651 to make it a bit more readable:

package SampleEnum type EFoo int const ( A EFoo = iota C T G ) type IEFoo interface { Get() EFoo } func(e EFoo) Get() EFoo { // every EFoo must fulfill the IEFoo interface return e } func(e EFoo) otherMethod() { // "private" //some logic } 

Comments

6

A simpler way I have found to work.

type TX int const ( Stake TX = iota Withdraw ) func (t TX) String() string { return [...]string{"STAKE", "WITHDRAW"}[t] } log.Println(Stake.String()) // --> STAKE 

Comments

5

Also, this is a pretty effective way to store different roles in one location in a byte, where the first value is set to 1, bit shifted by an iota.

package main import "fmt" const ( isCaptain = 1 << iota isTrooper isMedic canFlyMars canFlyJupiter canFlyMoon ) func main() { var roles byte = isCaptain | isMedic | canFlyJupiter //Prints a binary representation. fmt.Printf("%b\n", roles) fmt.Printf("%b\n", isCaptain) fmt.Printf("%b\n", isTrooper) fmt.Printf("%b\n", isMedic) fmt.Printf("Is Captain? %v\n", isCaptain&roles == isCaptain) fmt.Printf("Is Trooper? %v", isTrooper&roles == isTrooper) } 

Comments

5

I created the enum this way. Suppose we need an enum representing gender. Possible values are Male, Female, Others

package gender import ( "fmt" "strings" ) type Gender struct { g string } var ( Unknown = Gender{} Male = Gender{g: "male"} Female = Gender{g: "female"} Other = Gender{g: "other"} ) var genders = []Gender{ Unknown, Male, Female, Other, } func Parse(code string) (parsed Gender, err error) { for _, g := range genders { if g.g == strings.ToLower(code) { if g == Unknown { err = fmt.Errorf("unknown gender") } parsed = g return } } parsed = Unknown err = fmt.Errorf("unknown gender", code) return } func (g Gender) Gender() string { return g.g } 

Comments

4

This is a safe way to implement enum in golang:

package main import ( "fmt" ) const ( MALE = _gender(1) FEMALE = _gender(2) RED = _color("RED") GREEN = _color("GREEN") BLUE = _color("BLUE") ) type Gender interface { _isGender() Value() int } type _gender int func (_gender) _isGender() {} func (_g _gender) Value() int { return int(_g) } type Color interface { _isColor() Value() string } type _color string func (_color) _isColor() {} func (_c _color) Value() string { return string(_c) } func main() { genders := []Gender{MALE, FEMALE} colors := []Color{RED, GREEN, BLUE} fmt.Println("Colors =", colors) fmt.Println("Genders =", genders) } 

The output:

Colors = [RED GREEN BLUE] Genders = [1 2] 

Comments

1

I encountered a comparable challenge when working with enums, despite the existence of numerous well-articulated responses. I utilized the solution provided by @user3842449 and made some adjustments.

  1. The first modification was to enhance compiler safety for various Enums.

  2. Secondly, I optimized the lookup value to index and index to value time complexity to O(1).

  3. Lastly, I empowered callers to determine the ordinal and value of the Enum field.

This approach ensures greater flexibility, efficiency and effective implementation of enums. We can add more methods as needed either at the EnumItem or EnumType. Hopefully, it helps other developers.

Code: PlayGround(https://go.dev/play/p/fqJv4duDLVm)

// You can edit this code! // Click here and start typing. package main import ( "fmt" "golang.org/x/exp/maps" ) type EnumItem[T comparable] struct { Value string Index int } type EnumType[T comparable] struct { ItemToStringMap map[string]int StringToItemMap map[int]string } func NewEnumItem[T comparable](value string, index int) EnumItem[T] { return EnumItem[T]{ Value: value, Index: index, } } func NewEnumType[T comparable](items ...EnumItem[T]) *EnumType[T] { enumType := &EnumType[T]{ ItemToStringMap: make(map[string]int), StringToItemMap: make(map[int]string), } for _, item := range items { enumType.ItemToStringMap[item.Value] = item.Index enumType.StringToItemMap[item.Index] = item.Value } return enumType } func (e *EnumType[T]) GetItem(index int) string { return e.StringToItemMap[index] } func (e *EnumType[T]) GetIndex(value string) int { return e.ItemToStringMap[value] } func (e *EnumType[T]) GetItemByValue(value string) *EnumItem[T] { return &EnumItem[T]{ Value: value, Index: e.ItemToStringMap[value], } } func (e *EnumType[T]) GetItemByIndex(index int) string { return e.StringToItemMap[index] } func (e *EnumType[T]) Values() []string { return maps.Keys(e.ItemToStringMap) } func (e *EnumType[T]) Parse(value string) (*EnumItem[T], error) { index, ok := e.ItemToStringMap[value] if !ok || index == 0 { return nil, fmt.Errorf("invalid value: %s", value) } return &EnumItem[T]{ Value: value, Index: index, }, nil } func unknownInputFromClient() string { return "inprogress" } func main() { type ( JobStatus int OrderStatus int ) var ( jobStarted = NewEnumItem[JobStatus]("started", 10) jobInProgress = NewEnumItem[JobStatus]("inprogress", 10) jobCompleted = NewEnumItem[JobStatus]("completed", 10) orderPlaced = NewEnumItem[OrderStatus]("placed", 1) orderProcessing = NewEnumItem[OrderStatus]("processing", 2) orderCompleted = NewEnumItem[OrderStatus]("completed", 3) jobStatusType = NewEnumType[JobStatus](jobStarted, jobInProgress, jobCompleted) orderStatusType = NewEnumType[OrderStatus](orderPlaced, orderProcessing, orderCompleted) ) fmt.Printf("JobStatus enum values: %v\n", jobStatusType.Values()) fmt.Printf("OrderStatus enum values: %v\n", orderStatusType.Values()) unknownEnumItem, err := jobStatusType.Parse(unknownInputFromClient()) if err != nil { fmt.Printf("unknown enum value for jobStatusType, allowed values are: %v\n", jobStatusType.Values()) } else { switch unknownEnumItem.Value { case jobStarted.Value: fmt.Printf("Just started the Job\n") case jobInProgress.Value: fmt.Printf("Job status was set to InProgress\n") case jobCompleted.Value: fmt.Printf("Just completed the Job\n") } } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.