What is an idiomatic way of representing enums in Go?enums


Refering 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, so that 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

type Baser interface {
    Base() base

// every base must fullfill 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()

// 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 defined new instances.


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.

Golang: Creating a Constant Type and Restricting the Type's Values


Your proposed solution is not safe in a way you want it to be. One can use untyped integer constants to create new values of unary having a different int value than 1 or -1. See this example:

p := unary.Positive
fmt.Printf("%v %d\n", p, p)

p = 3
fmt.Printf("%v %d\n", p, p)

Output will be:

+ 1
- 3

We could change p's value to store the int value 3 which is obviously not equal to Positive nor to Negative. This is possible because Spec: Assignability:

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

  • ...
  • x is an untyped constant representable by a value of type T.

3 is an untyped constant, and it is representable by a value of type unary which has underlying type int.

In Go you can't have "safe" constants of which "outsider" packages cannot create new values of, for the above mentioned reason. Because if you want to declare constants in your package, you can only use expressions that have "untyped" versions - which may be used by other packages too in assignments (just as in our example).

Unexported struct

If you want to fulfill the "safe" part, you may use unexported structs, but then they cannot be used in constant declarations.


type unary struct {
    val int

var (
    Positive = unary{1}
    Negative = unary{-1}

func (u unary) String() string {
    if u == Positive {
        return "+"
    return "-"

func (u unary) CalExpr() int {
    return u.val

Attempting to change its value:

p := unary.Positive

p.val = 3 // Error: p.val undefied (cannot refer to unexported field or method val)

p = unary.unary{3} // Error: cannot refer to unexported name unary.unary
// Also error: implicit assignment of unexported field 'val' in unary.unary literal

Note that since we're now using a struct, we can further simplify our code by adding the string representation of our values to the struct:

type unary struct {
    val int
    str string

var (
    Positive = unary{1, "+"}
    Negative = unary{-1, "-"}

func (u unary) String() string { return u.str }

func (u unary) CalExpr() int { return u.val }

Note that this solution still has a "flaw": it uses exported global variables, whose values can be changed by other packages. It's true that other packages cannot create and assign new values, but they can do so with existing values, e.g.:

unary.Positive = unary.Negative

If you want to protect yourself from such misuse, you also have to make such global variables unexported. And then of course you have to create exported functions to expose those values, for example:

var (
    positive = unary{1}
    negative = unary{-1}

func Positive() unary { return positive }

func Negative() unary { return negative }

Then acquiring/using the values:

p := unary.Positive()


Care must be taken if you plan to use an interface type for your "constants". An example can be seen in Kaveh Shahbazian's answer. An unexported method is used to prevent others from implementing the interface, giving you the illusion that others truely can't implement it:

type Unary interface {
    CalExpr() int
    disabler() // implementing this interface outside this package is disabled

var (
    Positive Unary = unary(1)  // visible outside of the package unary
    Negative Unary = unary(-1) // visible outside of the package unary

type unary int // not visible outside of the package unary

func (u unary) disabler() {}

func (u unary) String() string { /* ... */ }

func (u unary) CalExpr() int { /* ... */ }

This is not the case however. With a dirty trick, this can be circumvented. The exported Unary type can be embedded, and an existing value can be used in order to implement the interface (along with the unexported method), and we can add our own implementations of the exported methods, doing / returning whatever we want to.

Here is how it may look like:

type MyUn struct {

func (m MyUn) String() string { return "/" }

func (m MyUn) CalExpr() int { return 3 }

Testing it:

p := unary.Positive
fmt.Printf("%v %d\n", p, p)

p = MyUn{p}
fmt.Printf("%v %d\n", p, p.CalExpr())


+ 1
/ 3

Special case

As Volker mentioned in his comment, in your special case you could just use

type unary bool

const (
    Positive unary = true
    Negative unary = false

As the type bool has two possible values: true and false, and we've used all. So there are no other values that could be "exploited" to create other values of our constant type.

But know that this can only be used if the number of constants is equal to the number of possible values of the type, so the usability of this technique is very limited.

Also keep in mind that this does not prevent such misuses when a type of unary is expected, and someone accidentally passes an untyped constant like true or false.

When should a type be a struct containing another type and when should it just “extend”(?) that type?

Struct with zero fields are handy. Structs with many fields are handy even more. Structs with exactly one field are a bit special and I can't think of a reasonably "good" case where to use them - even though they are seen regularly "in the wild". I, for one, don't use them.

Anyway, if you really really need tighter/bulletproof safety about the DNAStrand slice content - then it is possible to to use the single field struct and define an argument checking setter method for this/such named type.

In that case, if the definition is later used from some other package, there's no way, modulo using package unsafe, to circumvent the checks and get a result equivalent to your DNAStrand{[]byte("foo bar")} example.

I'd use type DNAStrand []byte because it's simple, and because I can use regexps on it. I'd probably use an initialisation function that checks that every byte is in ACGT though.

var validDNAStrandPat = regexp.MustCompile("[ACTG]*")

func DNAStrandForString(s string) DNAStrand {
    if !validDNAStrandPat.Match(s) {
        panic("Invalid DNA Strand.")
    return DNAStrand([]byte(s))