By carbocation


2013-01-20 16:07:22 8 Comments

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.

7 comments

@Aaron 2019-06-28 13:02:28

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"}}}

@metakeule 2013-08-01 09:17:48

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.

@Niriel 2013-09-29 15:21:25

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.

@anon58192932 2017-01-05 17:14:28

@metakeule I'm trying to understand your example but your choice in variable names has made it exceedingly difficult.

@wandermonk 2018-08-30 04:28:12

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)

}

@cbednarski 2019-05-23 04:27:45

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.

@Becca Petrin 2018-03-19 22:48:16

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.

@Azat 2017-11-21 11:05:08

You can make it so:

type MessageType int32

const (
    TEXT   MessageType = 0
    BINARY MessageType = 1
)

With this code compiler should check type of enum

@425nesp 2017-12-19 08:16:46

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.

@Jeremy Gailor 2018-01-02 22:54:06

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?

@waynethec 2018-01-30 18:35:42

@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

@Rodolfo Carvalho 2018-02-26 06:16:42

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.

@Moshe Revah 2016-01-02 13:56:02

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.

@S.R 2019-01-21 13:49:55

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.

@zzzz 2013-01-20 16:15:09

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.

@mna 2013-01-20 16:46:19

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.

@Deleplace 2013-01-20 17:31:39

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

@zzzz 2013-01-20 17:41:00

Go has no concept of numeric subrange types, like e.g. Pascal's has, so Ord(Base) is not limited to 0..3 but has the same limits as its underlying numeric type. It's a language design choice, compromise between safety and performance. Consider "safe" run time bound checks every time when touching a Base typed value. Or how should one define 'overflow' behavior of Base value for arithmetics and for ++ and --? Etc.

@mna 2013-01-21 01:56:13

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).

@weberc2 2013-01-23 15:51:59

@PuerkitoBio iota is incremented at every new line. So if you initialize two constants on the same line to iota, they will have the same value. If you skip a line (a la the example above) using _, the next assignment will be the value before the skip plus two.

@weberc2 2013-01-23 15:55:05

@jnml Ada implements Pascal-esque subranges with no serious performance hit (it performs close to C with runtime checks disabled). It seems like a fair compromise between performance and utility, in my mind at least. Then again, I don't think Ada has anything comparable to ++ or --.

@Marçal Juan 2014-07-25 08:32:13

You can use iota + 1 to not begin at 0.

@stewbasic 2015-10-09 00:13:56

@zzzz: If it were possible to define a type which allowed only comparison, a fixed set of provided values and iteration over that set (ie no arithmetic operations or casting), this would cover common uses of enums. All this can be checked at compile time and implemented as plain ints, so it would have safety and performance.

@turkenh 2019-07-19 00:24:49

Better to start with iota + 1 always by convention to distinguish initialised values vs first constant.

@John Dibling 2019-08-17 17:43:56

@MarçalJuan: You could also do _ = iota before the first assignment.

Related Questions

Sponsored Content

27 Answered Questions

[SOLVED] Get int value from enum in C#

  • 2009-06-03 06:46:39
  • jim
  • 1348011 View
  • 1631 Score
  • 27 Answer
  • Tags:   c# enums casting int

26 Answered Questions

[SOLVED] Cast int to enum in C#

  • 2008-08-27 03:58:21
  • lomaxx
  • 1218976 View
  • 2918 Score
  • 26 Answer
  • Tags:   c# enums casting

44 Answered Questions

[SOLVED] What is the preferred syntax for defining enums in JavaScript?

26 Answered Questions

[SOLVED] How do I enumerate an enum in C#?

13 Answered Questions

[SOLVED] What is a typedef enum in Objective-C?

26 Answered Questions

[SOLVED] How to get an enum value from a string value in Java?

  • 2009-03-02 22:56:34
  • Malachi
  • 1020478 View
  • 1846 Score
  • 26 Answer
  • Tags:   java enums

14 Answered Questions

[SOLVED] Comparing Java enum members: == or equals()?

  • 2009-11-17 17:26:27
  • Matt Ball
  • 600440 View
  • 1589 Score
  • 14 Answer
  • Tags:   java enums

43 Answered Questions

[SOLVED] How can I represent an 'Enum' in Python?

11 Answered Questions

[SOLVED] What does the [Flags] Enum Attribute mean in C#?

  • 2008-08-12 04:09:16
  • Brian Leahy
  • 443888 View
  • 1338 Score
  • 11 Answer
  • Tags:   c# enums flags

13 Answered Questions

[SOLVED] Enum "Inheritance"

  • 2009-04-16 19:27:07
  • CodeMonkey1313
  • 205609 View
  • 361 Score
  • 13 Answer
  • Tags:   c# .net enums

Sponsored Content