Skip to content

Latest commit

 

History

History
151 lines (125 loc) · 3.61 KB

enums.md

File metadata and controls

151 lines (125 loc) · 3.61 KB

Overview

  1. How enums work
  2. iota (not itoa)
  3. See also stringer doc
  4. Enums are relatively annoying to use
    1. Compare to Rust enum or to Java enum
    2. eg. does the compiler enforce switch statements are comprehensive? (not for golang)
    3. eg. can the enum have properties? (not for golang)

iota

  1. iota is a constant
    1. only works inside a const declaration
  2. Starts at 0 (not 1)

Enum Definition Example

  1. Zero value is UnknownFoo
  2. Suffix Unknown with distinct type to void collision with other enums in the package
type GameDirection uint8

const (
    UnknownDirection = GameDirection(iota)
    Up
    Down
    Left
    Right
)

// Helper method example
func (d GameDirection) IsHorizontal() bool {
    switch d {
    case Left, Right:
        return true

    default:
        //NOTE: Unknown case is here
        return false
    }
}

String/Labels Example

Using a Map

  1. Pro: simpler to maintain than switch approach
  2. Pro: simpler reverse lookups
  3. Pro: (sligtly) less code
  4. Con: Indirect lookups
var directionLabels = map[GameDirection]string{
    Down:  "Down",
    Left:  "Left",
    Right: "Right",
    Up:    "Up",
}

// NOTE: You can also generate these via stringer program
// signature matches fmt.Stringer interface
func (d GameDirection) String() string {
    s, ok := directionLabels[d]
    if !ok {
        return "N/A"
    }

    return s
}

Using a Switch

  1. Pro: Faster than map approach
  2. Con: harder to maintain
  3. Con: harder to do reverse lookups
// signature matches fmt.Stringer interface
func (d GameDirection) String() string {
    switch d {
    case Down:
        return "Down"
    case Left:
        return "Left"
    case Right:
        return "Right"
    case Up:
        return "Up"
    default:
        return "N/A"
    }
}

Lookup Function Examples

func FromLabel(label string) GameDirection {
    candidate := strings.TrimSpace(label)

    for key, lbl := range directionLabels {
        if strings.EqualFold(lbl, candidate) {
            return key
        }
    }

    return Unknown
}

func FromValue(value uint8) GameDirection {
    switch GameDirection(value) {
    case Down:
        return Down
    case Left:
        return Left
    case Right:
        return Right
    case Up:
        return Up

    default:
        return Unknown
    }
}

Iteration Example

TODO: do I need this?

Idioms

  1. Prefer unsigned numeric type
  2. Declare a new type (not an alias)
    1. Compiler distinguishes from other enums
  3. Start with Unknown (as zero value)
    1. Helps detect unset value
    2. Helps integrate with gRPC and Protocol Buffers
  • TODO: catching missing branches in enum switch

Other Resources

  1. Official docs
  2. https://threedots.tech/post/safer-enums-in-go/
  3. https://dlintw.github.io/gobyexample/public/constants-and-iota.html
  4. https://yourbasic.org/golang/iota/
  5. https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
  6. https://golangbyexample.com/iota-in-golang/
  7. https://medium.com/swlh/iota-create-effective-constants-in-golang-b399f94aac31