128

★ Ultimate Visual Guide to Go Enums ★ – Learn Go Programming

 6 years ago
source link: https://blog.learngoprogramming.com/golang-const-type-enums-iota-bc4befd096d3?gi=ca3b20fed31
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

★ Ultimate Visual Guide to Go Enums and iota

Go enums and iota — Full of tips and tricks with runnable code examples.

Important: In this tutorial, you’ll learn how to use the enums without iota in the first section — After that, you’ll also learn about iota and the other helper tools as well after the first section. So, learn to crawl first, then walk then run!

1*Cjrs2jaureuKeE4OrqzNqQ.png

What is an Enum? ★

An enum groups related constants together in one type.

Example enums:

  • Timezones: EST, CST…
  • T-shirt Sizes: Small, Medium, Large
  • Server Statuses: Unknown, Running, Stopped, Resumed
1*3XRDFiXgLz__mqXZqXKMgg.png

Why do we need enums?

  • Grouping and expecting only some related values
  • Sharing common behavior
  • Avoids using invalid values
  • To increase the code readability and the maintainability
1*3XRDFiXgLz__mqXZqXKMgg.png

How can you imitate an enum in Go?

For example, suppose that you want to create an enum for the weekdays.

1*-IYYWJQynajSdLqHEE9Hsw.png

Each constant will have the same type: Weekday

1*3XRDFiXgLz__mqXZqXKMgg.png

★ First: Declare a new custom type: Weekday

Weekday will unify our enum constants under a common type.

type Weekday int
1*3XRDFiXgLz__mqXZqXKMgg.png

★ Second: Declare related constants for Weekday

Assign them different numeric values, so, they don’t clash.

const (
Sunday Weekday = 0
Monday Weekday = 1
Tuesday Weekday = 2
Wednesday Weekday = 3
Thursday Weekday = 4
Friday Weekday = 5
Saturday Weekday = 6
)fmt.Println(Sunday) // prints 0fmt.Println(Saturday) // prints 6
1*3XRDFiXgLz__mqXZqXKMgg.png

Creating common behavior for Weekday enum

You attach methods to a type to define its behaviors.

The attached methods will be the inseparable parts of Weekday, and shared among Weekday constants.

1*1Z0xKpZ_gl2Ci53cwuo71w.png
1*3XRDFiXgLz__mqXZqXKMgg.png

★String() method:

func (day Weekday) String() string {
// declare an array of strings
// ... operator counts how many
// items in the array (7)
names := [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"} // → `day`: It's one of the
// values of Weekday constants.
// If the constant is
Sunday,
// then
day is 0.
//
// prevent panicking in case of
// `day` is out of range of Weekday
if day < Sunday || day > Saturday {
return "Unknown"
} // return the name of a Weekday
// constant from the names array
// above.
return names[day]
}

★Let’s try:

fmt.Printf("Which day it is? %s\n", Sunday)
//
Which day it is? Sunday

We’re done adding String() method. Play with the code to understand it better.

1*3XRDFiXgLz__mqXZqXKMgg.png

★Weekend() method:

func (day Weekday) Weekend() bool {
switch day { case Sunday, Saturday: // If day is a weekend day
return true default: // If day is not a weekend day
return false
}
}

Convention: Instead of IsWeekend, we just use Weekend.

★Let’s try:

fmt.Printf("Is Saturday a weekend day? %t\n", Saturday.Weekend())
//
Is Saturday a weekend day? true

We attached the Weekend method to Weekday type. Go try it.

1*3XRDFiXgLz__mqXZqXKMgg.png

Creating names automatically

1*61qs93xP7voMUnd4K_yFvw.png

You can use Rob Pike’s Stringer to create String method automatically.

# install the stringer tool
go get -u -a golang.org/x/tools/cmd/stringer# grab the code from here
# and save it in weekday.go
# inside your $GOPATH/src/enums
# then, run stringer to create
# enum names automatically
stringer -type Weekday weekdays.go
1*l5UT3eLUTSwxvpVCr1Bv5g.png

Automate enum values with: iota

→ A numeric universal counter starting at 0

→ Used only with constant declarations

→ Can be shadowed. Please, don’t do that.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

How to use iota?

Iota expression is repeated by the other constants until another assignment or type declaration shows up.

1*-bm86GEiW0gNnIh_3bSSmg.png

Each constant’s type will be Weekday and each will execute their own iota + 1.To see how iota behaves be sure to try the example.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Let’s see how iota ticks tocks

iota increases by 1 after each line except empty and comment lines.

1*sfAHT3zk-WjxSDRIDMpdaA.gif
1*l5UT3eLUTSwxvpVCr1Bv5g.png

When not to use iota

const (
RestartMarkerReply = 110
ServiceReadyInNMinutes = 120
CommandOK = 200
CommandNotImplemented = 202
// ...
)

Don’t use iota for a list of predefined values like FTP server status codes.

const (
Fatal = iota
)

Just assign 0 there. It’s harmless but it’s unnecessary either.

1*l5UT3eLUTSwxvpVCr1Bv5g.png
1*hPw_sxpJqUdWXVCMjxx7IQ.png

This section will enhance your understanding of iota.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

A basic expression

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota)

// iota: 1, CST: -6
CST

// iota: 2, MST: -7
MST

// iota: 3, MST: -8
PST
)

You can use any constant expression with iota.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Resetting iota

// iota reset: it will be 0.
const
(
Zero = iota // Zero = 0
One // One = 1
)// iota reset: will be 0 again
const
(
Two = iota // Two = 0
)// iota: reset
const Three = iota // Three = 0

Whenever a const keyword appears iota resets to zero.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Skipping some values

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota) // _ is the blank identifier
//
iota: 1 _ // iota: 2, MST: -7
MST // iota: 3, MST: -8
PST
)

You can skip some lines with a blank identifier, but, iota will keep increasing.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

About iota’s behavior on comment and empty lines

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota) // On a comment or an empty
// line, iota will not
// increase
// iota: 1, CST: -6
CST // iota: 2, MST: -7
MST // iota: 3, MST: -8
PST
)

iota will not increase on comments or empty lines.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Using iota in the middle

const (
One = 1
Two = 2 // Three = 2 + 1 => 3
// iota in the middle
Three = iota + 1 // Four = 3 + 1 => 4
Four
)

Iota can be used on any line inside a const declaration. Here it‘s 3 instead of 0.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Multiple iotas in a single line

const (
// Active = 0, Moving = 0,
// Running = 0

Active, Moving, Running = iota, iota, iota // Passive = 1, Stopped = 1,
// Stale = 1

Passive, Stopped, Stale
)

In the same line, all constants will get the same iota values.

const (
// Active = 0, Running = 100
Active, Running = iota, iota + 100 // Passive = 1, Stopped = 101
Passive, Stopped // You can't declare like this.
// The last expression will be
// repeated
CantDeclare // But, you can reset
// the last expression
Reset = iota // You can use any other
// expression even without iota
AnyOther = 10
)

By using new expressions you can stop the previously repeating expression.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Repeating and canceling expressions

const (
// iota: 0, One: 1 (type: int64)
One int64 = iota + 1 // iota: 1, Two: 2 (type: int64)
// Two will be declared as if:
// Two int64 = iota + 1
Two // iota: 2, Four: 4 (type: int32)
Four int32 = iota + 2 // iota: 3, Five: 5 (type: int32)
// Five will be declared as if:
// Five int32 = iota + 2
Five // (type: int)
Six = 6 // (type: int)
// Seven will be declared as if:
// Seven = 6
Seven
)

The last used expression and type will be repeated.

You can group constants with different expressions.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Evens and odds

type Even boolconst (
// 0 % 2 == 0 ==> Even(true)
a = Even(iota % 2 == 0) // 1 % 2 == 0 ==> Even(false)
b // 2 % 2 == 0 ==> Even(true)
c // 3 % 2 == 0 ==> Even(false)
d
)

`==` operator converts the iota expression to bool type.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Counting backward

const (
max = 10
)const (
a = (max - iota) // 10
b // 9
c // 8
)

Go stdlib text scanner also uses this pattern.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Producing Alphabets

const (
// string will convert the
// expression into string.
//
// or, it'll assign character
// codes.
a = string(iota + 'a') // a
b // b
c // c
d // d
e // e
)
1*l5UT3eLUTSwxvpVCr1Bv5g.png

Bitwise operations

type Month intconst (
// 1 << 0 ==> 1
January Month = 1 << iota February // 1 << 1 ==> 2
March // 1 << 2 ==> 4
April // 1 << 3 ==> 8
May // 1 << 4 ==> 16
June // ...
July
August
September
October
November
December // Break the iota chain here. // AllMonths will have only
// the assigned month values,
// not the iota's.
AllMonths = January | February |
March | April | May | June |
July | August | September |
October | November |
December
)

With the powers of 2, this code creates an all emcompassing constant from the previous constants.

You don’t need to use iota for every constant in an enum, you can overwrite the values manually.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Beware the zero-value

type Activity intconst (
Sleeping = iota
Walking
Running
)func main() {
var activity Activity
// activity initialized to
// its zero-value of int
// which is
Sleeping
}

0 is a zero-value for integers. So, you can’t know whether the Activity is initialized or not; Is it really in the Sleeping state?

The code that uses zero-based iota values.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

iota + 1 trick

const (
Sleeping = iota + 1
Walking
Running
)func main() {
var activity Activity
// activity will be zero,
// so it's not initialized
activity = Sleeping
// now you know that it's been
// initialized

}

Use “iota + 1” to be sure that the enum type is initialized.

The code that uses one-based iota values.

1*l5UT3eLUTSwxvpVCr1Bv5g.png

Unknown state pattern

const (
Unknown = iota
Sleeping
Walking
Running
)

Start with an “Unknown” to be sure of enum’s initialization.

The code that uses a default Unknown value.

1*l5UT3eLUTSwxvpVCr1Bv5g.png
1*kUIBd_rPd2chGkqD3_U8Xg.png

▶︎ In the Greek alphabet, iota is the ninth and the smallest letter (in terms of its visual representation). Pronounced like this. It represents a very small quantity of something.

In vector math, and in some programming languages, it’s been used to generate an array of consecutive integers as in 1, 2, 3, 4… ⟧

If you’re curious just like me, you can read the implementation of constants, and iota in Go source code. Iota is declared here as const iota = 0. And, the constant implementation is here, and the behavior of incrementing iota is here.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK