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