754

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.

Philip Kirkbride
  • 21,381
  • 38
  • 125
  • 225
carbocation
  • 8,806
  • 7
  • 25
  • 30
  • 5
    In go standard packages they're represented as constants. See http://golang.org/pkg/os/#pkg-constants – Denys Séguret Jan 20 '13 at 16:11
  • 2
    related: http://stackoverflow.com/questions/14236263/when-should-a-type-be-a-struct-containing-another-type-and-when-should-it-just – lbonn Jan 20 '13 at 16:12
  • 1
    @carbocation: That's not how duplicates work on [so]. Questions which are duplicates should be closed as a duplicate of the question with the *best content*, not the earliest one. – Jörg W Mittag Jan 21 '22 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. – carbocation May 08 '22 at 17:04

14 Answers14

888

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.

zzzz
  • 87,403
  • 16
  • 175
  • 139
  • 26
    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. – mna Jan 20 '13 at 16:46
  • 38
    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 : http://play.golang.org/p/oH7eiXBxhR – Deleplace Jan 20 '13 at 17:31
  • 8
    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). – mna Jan 21 '13 at 01:56
  • 14
    You can use ```iota + 1``` to not begin at 0. – Marçal Juan Jul 25 '14 at 08:32
  • 3
    Note that in your last example, C, T, G are untyped numeric consts, not type Base. For all consts to be Base, you need to repeat Base on each line. – Kosta Jun 10 '20 at 12:41
  • 3
    @Kosta in the current version with `A base = iota`, all the values correctly have the type `Base`. If they were untyped numeric consts, we would be able to use them as ints, but we can't https://play.golang.org/p/Dpop4S3qdNs – Deleplace Jun 28 '21 at 10:38
  • 2
    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. – ooouuiii Jul 23 '21 at 16:17
  • @MarçalJuan Or start with `_ = iota` to skip zero entirely. – iBug Aug 24 '22 at 18:56
  • `type Base int` is a useless line of code, because `Base` is still assignment compatible to `int`. – ceving Jan 26 '23 at 17:02
  • @ceving no, it is not useless. It prevents you from accidentally mixing it up with other types: https://go.dev/play/p/Ta3LDmVSnev – Hofsiedge Aug 11 '23 at 16:41
117

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.

Taavi
  • 135
  • 2
  • 16
metakeule
  • 3,724
  • 2
  • 23
  • 29
  • 13
    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. – Niriel Sep 29 '13 at 15:21
  • 23
    @metakeule I'm trying to understand your example but your choice in variable names has made it exceedingly difficult. – fIwJlxSzApHEZIl Jan 05 '17 at 17:14
  • 4
    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! – Graham Nicholls Feb 28 '20 at 17:40
65

You can make it so:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

With this code compiler should check type of enum

Azat
  • 1,048
  • 8
  • 9
  • 18
    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. – 425nesp Dec 19 '17 at 08:16
  • 3
    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? – Jeremy Gailor Jan 02 '18 at 22:54
  • @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](https://github.com/golang/go/wiki/CodeReviewComments#mixed-caps) and [Effective Go Section on Constants](https://golang.org/doc/effective_go.html#constants) – waynethec Jan 30 '18 at 18:35
  • There is a preference. Just like variables, functions, types and others, constant names should be mixedCaps or MixedCaps, not ALLCAPS. Source: [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments#mixed-caps). – Rodolfo Carvalho Feb 26 '18 at 06:16
  • 2
    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. – Kosta Jun 10 '20 at 12:43
  • @RodolfoCarvalho interestingly enough https://go.dev/doc/effective_go#constants has constant names that are only upper case – serverhorror Nov 24 '21 at 17:59
  • @MartinM. I'd call that an "accident" -- those constants (KB, MB, GB, etc) just happen to be abbreviations (see https://github.com/golang/go/wiki/CodeReviewComments#initialisms) to well-known units. – Rodolfo Carvalho Nov 26 '21 at 14:27
62

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.

Becca Petrin
  • 1,494
  • 14
  • 13
41

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",
}
Yu Huang
  • 3,085
  • 2
  • 24
  • 22
  • This is syntactically nice, but I'm paranoid about people changing constants. See this example: https://play.golang.org/p/9D1tMQJVmIc . If namespacing is important, I would lean towards placing them in their own package. – Grokify Jul 16 '21 at 06:48
  • How can I use that as an enum? – Bruno Negrão Zica Jul 17 '21 at 03:30
  • 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. – Gurleen Sethi Oct 10 '21 at 23:27
22

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.

Zippo
  • 15,850
  • 10
  • 60
  • 58
  • Do u know is oposite solution. I mean string -> MyType. Since one way solution is far from ideal. [Here](https://gist.github.com/lummie/7f5c237a17853c031a57277371528e87) is sb gist that do what I want - but writing by hand is easy to make mistakes. – S.R Jan 21 '19 at 13:49
  • Adding a code-sample in the answer would definately help getting your point across more cleanly. – XDS Jul 25 '22 at 08:28
20

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

Grokify
  • 15,092
  • 6
  • 60
  • 81
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)

}
wandermonk
  • 6,856
  • 6
  • 43
  • 93
  • 5
    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. – cbednarski May 23 '19 at 04:27
  • 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. – bfontaine Oct 11 '22 at 15:21
  • 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. – kafran Mar 13 '23 at 20:14
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"}}}
adiga
  • 34,372
  • 9
  • 61
  • 83
6

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
}
XDS
  • 3,786
  • 2
  • 36
  • 56
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
}
kaushik
  • 2,308
  • 6
  • 35
  • 50
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]
BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28
4

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)

}
monkrus
  • 1,470
  • 24
  • 23
1

A simpler way I have found to work.

const (
Stake TX = iota
Withdraw)


type TX int

func (t TX) String() string {
return [...]string{"STAKE", "WITHDRAW"}[t]}

log.Println(Stake.String()) --> STAKE
Jon Gregis
  • 49
  • 2