0

We generally use Enums to represent states.

Eg, in python: we do

class QueueState(Enum):
    Enqueued = 1
    Processing = 2
    Processed = 3
    Dequeued = 4

And we can access them using QueueState.Enqueued, etc. The same kind of behavior is present in other languages also, like Java, C#, etc. I mean these states are kinda bound within QueueState.

But when it comes to declaring states in go, we use const and iota, eg:

type QueueState int

const (
    Enqueued QueueState = iota
    Processing
    Processed
    Dequeued
)

I see there is no binding of these states (Enqueued, Processing, etc) with the type QueueState.

To access them, I just need to use them as a constant variable.

Eg:

fmt.Println(Enqueued) // prints 0

Is there a way I can bind these states into a type and treat them as an enum as we do in other programming languages? Eg: I wanted to use them something like this QueueState.Enqueued

Amit Upadhyay
  • 7,179
  • 4
  • 43
  • 57
  • Can you show an example of a behaviour you'd like to have but does not exist? (Besides the constants not being in their own namespace like `QueueState.Enqueued`) – mkrieger1 Aug 02 '19 at 14:04
  • @mkrieger1 nope, I just wanted to know the design we use for representing states in go, `icza` answer is a great help. – Amit Upadhyay Aug 02 '19 at 14:11

1 Answers1

4

I see there is no binding of these states (Enqueued, Processing, etc) with the type QueueState.

This is not completely true. When you print its value, you see 0 printed because that's its numerical value. The type QueueState has int as its underlying type. But Enqueued is of type QueueState (try it on the Go Playground):

fmt.Printf("%T", Enqueued) // main.QueueState

If you want to "visually" bound it to the QueueState type, include it in its name:

type QueueState int

const (
    QueueStateEnqueued QueueState = iota
    QueueStateProcessing
    QueueStateProcessed
    QueueStateDequeued
)

Then when it is referred: QueueStateEnqueued it becomes obvious. This naming "technique" is widely used in the standard library, some examples from the net/http package:

const (
        MethodGet     = "GET"
        MethodHead    = "HEAD"
        MethodPost    = "POST"
        ...
)

const (
        StatusContinue           = 100 // RFC 7231, 6.2.1
        StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
        StatusProcessing         = 102 // RFC 2518, 10.1

        StatusOK                   = 200 // RFC 7231, 6.3.1
        StatusCreated              = 201 // RFC 7231, 6.3.2
        ...
)

If you want human-readable printed value, define a String() string method for it:

type QueueState int

func (s QueueState) String() string {
    switch s {
    case QueueStateEnqueued:
        return "Enqueued"
    case QueueStateProcessing:
        return "Processing"
    case QueueStateProcessed:
        return "Processed"
    case QueueStateDequeued:
        return "Dequeued"
    }
    return ""
}

Then when printed (try it on the Go Playground):

fmt.Println(QueueStateEnqueued) // prints Enqueued

Yes, it's not very convenient to provide this String() method and keep it updated, hence why tools like stringer exist. They generate this String() method in a more compact and efficient way than the above example implementation.

There's also the option to use string as the enum's underlying type, and the enum values will serve as the string representation without the String() method (try it on the Go Playground):

type QueueState string

const (
    QueueStateEnqueued   QueueState = "Enqueued"
    QueueStateProcessing QueueState = "Processing"
    QueueStateProcessed  QueueState = "Processed"
    QueueStateDequeued   QueueState = "Dequeued"
)

func main() {
    fmt.Println(QueueStateEnqueued) // prints Enqueued
}

Also note that when others refer to your enum values, they do so using the package name. So you may place the enum constants in their designated package, e.g. called queuestate, and then you may name your constants just Enqueued, Processing etc, but when they are referred to, it will be in the form of queuestate.Enqueued, queuestate.Processing etc.

Also note that using constants only you can't restrict the values of your type. For details, see Creating a Constant Type and Restricting the Type's Values.

icza
  • 389,944
  • 63
  • 907
  • 827