Professional Documents
Culture Documents
go enum
go enum
go enum
com/chap-27-enum-iota-and-bitmask
• Enumeration type
• iota
• index
• Byte, bits
• Binary
• Bitmask, Bitflag
3 Enum definition
An “enum” (or enumeration data type) is a data type that consists of a set of “values that are explicitly defined by the programmer”
[@institute1990ieee]
For example, days of the week are an enum. There are seven days of the week and not more.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
4 Go and enums
The language (at least in its version 1) does not have specific enumeration types. However, we can still build a type that offers the same
features as enums.
1 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
// enum-iota-bitmasks/type-as-enum/main.go
const (
GET HTTPMethod = 0
POST HTTPMethod = 1
PUT HTTPMethod = 2
DELETE HTTPMethod = 3
PATCH HTTPMethod = 4
HEAD HTTPMethod = 5
OPTIONS HTTPMethod = 6
TRACE HTTPMethod = 7
CONNECT HTTPMethod = 8
)
• Each constant is of type HTTPMethod and is named like the HTTP method its designates.
• It’s generally more efficient to compare an integer with another integer instead of comparing a string with another string
In this function, you expect the user to give you an HTTP method, and you expect to handle a predefined set of methods. Your function can
have a parameter of type HTTPMethod (instead of int ) :
Inside the function body, you can adapt the behavior of your handler by using the enum values :
Enums types can also be used in structs (like any other types) :
// enum-iota-bitmasks/type-as-enum/main.go
6 A perfectible solution
The solution proposed in the previous section is not perfect. Here is why :
2 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
lolMethod := HTTPMethod(42)
headers := make(map[string]string)
handle(lolMethod,headers,"/prices" )
We created a variable lolMethod which is of type HTTPMethod and the code will compile.
log.Println(GET)
// > 0
• When marshaled to JSON, a value of type HTTPMethod will be printed as an int ...
{
"method": 0,
"headers": {
"Accept": "application/json"
},
"uri": "/prices"
}
jsonB := []byte("{\"method\":\"GET\",\"headers\":{\"Accept\":\"application/json\"},\"uri\":\"/prices\"}")
req := HTTPRequest{}
err = json.Unmarshal(jsonB,&req)
if err != nil {
panic(err)
}
We got a panic :
panic: json: cannot unmarshal string into Go struct field HTTPRequest.method of type main.HTTPMethod
This is perfectly normal, we have a string as input, and we want to convert it to an int. One solution could be to change the underlying type to
string.
7 Enum libraries
To solve the previous issues :
3 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
// enum-iota-bitmasks/enum-implementations/main.go
However, this is tedious. We can use a library to do that for us. After a quick search on GitHub, it seems that two libraries emerge :
• https://github.com/alvaroloes/enumer
• https://github.com/abice/go-enum
They will generate those methods for you. They offer a command-line interface.
Note that the support for enums is a feature request for version 2 of the language.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
8 Iota
In the previous section:
To avoid those two tasks, we can modify our enum to use iota :
// enum-iota-bitmasks/iota/main.go
package main
import "fmt"
const (
GET HTTPMethod = iota
POST HTTPMethod = iota
PUT HTTPMethod = iota
DELETE HTTPMethod = iota
PATCH HTTPMethod = iota
HEAD HTTPMethod = iota
OPTIONS HTTPMethod = iota
TRACE HTTPMethod = iota
CONNECT HTTPMethod = iota
)
func main() {
fmt.Println(PUT)
// 2
}
We can simplify this code. It’s possible to avoid repeating HTTPMethod = iota each time by using the implicit repetition property. This
4 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
property is very handy; it says that inside a constant set ( const(...) ) you can assign a value just to the first const:
// enum-iota-bitmasks/iota-improvement/main.go
const (
GET HTTPMethod = iota
POST
PUT
DELETE
PATCH
HEAD
OPTIONS
TRACE
CONNECT
)
• It means that iota is always an integer; it’s impossible to use iota to construct floats values (for instance).
• “Its value is the index of the respective ConstSpec in that constant declaration”[@go-specs]
The value of iota is determined by the index of the constant in the constant declaration. In the previous example, POST is the second constant
then it has a value of 1. Why 1? Why not 2? That’s because of the third property of iota :
const (
First TestEnum = iota
Second
)
fmt.Println(First)
// 0
iota is initialized with zero. If you want that your first enum element starts with something other than zero, you can assign a different value to
the initial constant :
const (
First TestEnum = iota +4
Second
)
fmt.Println(First)
// 4 (0+4)
const (
First TestEnum = iota * 3
Second
)
fmt.Println(Second)
// 3 (1*3)
5 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
iota value
9 Byte / Bit
A byte is composed of 8 bits of memory. A bit is a binary digit. It’s either equal to 0 or 1.
The indexing of bits is not natural; we start counting from right to left (and not left to right).
10 Operations on bits
The first step before jumping to bitwise operators is to learn how to print the binary representation of a number.
// enum-iota-bitmasks/print-bits/main.go
package main
import "fmt"
func main() {
var x uint8
x = 1
fmt.Printf("%08b\n", x)
//00000001
x = 2
fmt.Printf("%08b\n", x)
//00000010
x = 255
fmt.Printf("%08b\n", x)
//11111111
}
6 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
In the previous code listing, we define X, an unsigned integer stored on 8 bits. X can take the values from 0 to 255. We assign to x the value 1,
then 2, and 255. Each time we are printing the binary representation.
• The operator
• The operands
• The result
| is the operator
z is the result For each operator, the same logic applies. Operations are performed independently on each bit of the operands.
Bitwise operators do not have to be mixed up with logical operators (&&, || and !). Those operators are used to compare boolean values from
left to right.
• Warning: you can use the following operators with integers only.
• ust remember that it’s only true if both operands are true.
x y x&y
00 0
01 0
11 1
10 0
AND
bitwise
operator
// enum-iota-bitmasks/bitwise-operations/main.go
var x, y, z uint8
x = 1 // 00000001
y = 2 // 00000010
z = x & y
// print in binary
fmt.Printf("%08b\n", z)
// 00000000
// print in base 10
fmt.Printf("%d\n", z)
// 0
7 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
As shown in figure 2 we take each bit individually, and then we are calculating the result of every; that’s single operation : 1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
0 & 0 = 0
0 & 0 = 0
0 & 0 = 0
0 & 0 = 0
0 & 0 = 0 Note that we begin by the right side. Get used to it; that’s the rule when you manipulate bits.
// enum-iota-bitmasks/bitwise-operation-and/main.go
var x, y, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
y = 100 // 01100100
fmt.Printf("%08b\n", y)
z = x & y
// print in binary
fmt.Printf("%08b\n", z)
// 01000000
// print in base 10
fmt.Printf("%d\n", z)
// 64
In the figure 3 you can see the detail of the operation 200&100.
8 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
10.2.2 OR operator : |
x | y is equal to 1 if :
x y x|y
00 0
01 1
11 1
10 1
OR
bitwise
operator
Here is an example in go :
// enum-iota-bitmasks/bitwise-or/main.go
var x, y, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
y = 100 // 01100100
fmt.Printf("%08b\n", y)
z = x | y
// print in binary
fmt.Printf("%08b\n", z)
// 11101100
// print in base 10
fmt.Printf("%d\n", z)
// 236
9 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
In the figure 4 you can see that we apply the or operation bit by bit. Starting from the right : 0 | 0 = 0
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
0 | 0 = 0
0 | 1 = 1
1 | 1 = 1
0 | 1 = 1 When we use the base ten notation for the operands and the result, it does not make sense. Let’s use the binary notation :
With OR, we set to 1 the bits of the left operand that are set to 1 in the right operand. (you can switch left and right, and this will also work).
When both operands are true, the result is true. Sometimes this behavior isn’t acceptable. We want maybe to exclude that case.
With the exclusive or : 1 ^ 1 = 0 . To be more precise, we have the following rule that applies: x ^ y is equal to 1 only if one of the
operands is equal to 1, not both.
10 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
// enum-iota-bitmasks/bitwise-xor/main.go
var x, y, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
y = 100 // 01100100
fmt.Printf("%08b\n", y)
z = x ^ y
// print in binary
fmt.Printf("%08b\n", z)
// 10101100
// print in base 10
fmt.Printf("%d\n", z)
// 172
The XOR set to 0 the bits that are identical in the two operands and set to 1 the bits that are different.
• ^0 = 1
• ^1 = 0
NOT will invert the values of the bits. Let’s take an example :
// enum-iota-bitmasks/bitwise-not/main.go
var x, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
z = ^x
// print in binary
fmt.Printf("%08b\n", z)
// 00110111
// print in base 10
fmt.Printf("%d\n", z)
// 55
11 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
This operator mixes the operator AND and NOT. We can decompose it :
• x AND (NOT y)
Let’s try to obtain the same result as before with this decomposition :
Let’s denote :
• x = 11001000
• y = 11111111
• NOT y = 00000000
You have to specify the number of positions by which the bits will be shifted.
var x, n, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
// number of positions
n = 1
z = x << n
// print in binary
fmt.Printf("%08b\n", z)
// 10010000
// print in base 10
fmt.Printf("%d\n", z)
// 144
In the previous example, we are left-shifting the byte 11001000 by one position. The result of this shift is 10010000. We have added a zero at
the left of the byte and shifted the other by 1 position as you can see in the figure 7.
12 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
We are storing our integers into 8 bits, so we are losing bits. to avoid that, we can store our numbers on 16 bits (2 bytes) :
var x, n, z uint16
x = 200 // 11001000
fmt.Printf("%08b\n", x)
// number of positions
n = 1
z = x << n
// print in binary
fmt.Printf("%b\n", z)
// 110010000
// print in base 10
fmt.Printf("%d\n", z)
// 400
When you left shift the binary representation of a number by n position you multiply it’s decimal representation bt it by two at the power n.
For instance, if you left shift by 3, you will multiply your number by two at the power three, which is 8 :
var x, n, z uint16
x = 200 // 11001000
fmt.Printf("%08b\n", x)
// number of positions
n = 3
z = x << n
// print in binary
fmt.Printf("%b\n", z)
// 11001000000
// print in base 10
fmt.Printf("%d\n", z)
// 1600
13 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
// enum-iota-bitmasks/bitwise-right-shift/main.go
var x, n, z uint8
x = 200 // 11001000
fmt.Printf("%08b\n", x)
// number of positions
n = 3
z = x >> n
// print in binary
fmt.Printf("%08b\n", z)
// 00011001
// print in base 10
fmt.Printf("%d\n", z)
// 25
You can see here that we are dividing 200 by 3 when we shift the bytes to the right. This is another property of binary numbers. When you
left shift the binary representation of a number (in base 10) by n position, you divide it by two at the power n.
11 Bitmasks (advanced)[sec:Bitmasks]
Imagine that you are building a function that requires eight boolean values to be configured :
4. Is logger activated?
8. Reboot on failure?
What we can do instead is passing just an integer value. This integer value can represent the configuration.
For instance, 01110001 (113 in base 10) will represent the following configuration :
1. Activated (1)
2. Deactivated (0)
3. Deactivated (0)
4. Deactivated (0)
5. Activated (1)
6. Activated (1)
7. Activated (1)
8. Deactivated (0)
Remember that when bytes are involved, we read from right to left.
• We spare memory. If we had eight booleans, we have 8*8 bits = 64 bits used compared to only 8.
We will use the left shift bitwise operation. The idea is the first configuration value is equal to 1 (00000001), and then for each new
configuration variable, we will left shift the bit :
14 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
const (
VERBOSE MyConf = 1 << iota
CONFIG_FROM_DISK
DATABASE_REQUIRED
LOGGER_ACTIVATED
DEBUG
FLOAT_SUPPORT
RECOVERY_MODE
REBOOT_ON_FAILURE
)
The values that are generated by this constant declaration are the following :
• VERBOSE : 00000001
• CONFIG_FROM_DISK : 00000010
• DATABASE_REQUIRED : 00000100
• LOGGER_ACTIVATED : 00001000
• DEBUG : 00010000
• FLOAT_SUPPORT : 00100000
• RECOVERY_MODE : 01000000
• REBOOT_ON_FAILURE : 10000000
We are left shifting the byte for each new constant by an iota. Remember that iota’s value increases when there is a new constant declared.
The code listing above is equivalent to :
// enum-iota-bitmasks/flags/main.go
const (
VERBOSE MyConf = 1 << 0
CONFIG_FROM_DISK MyConf = 1 << 1
DATABASE_REQUIRED MyConf = 1 << 2
LOGGER_ACTIVATED MyConf = 1 << 3
DEBUG MyConf = 1 << 4
FLOAT_SUPPORT MyConf = 1 << 5
RECOVERY_MODE MyConf = 1 << 6
REBOOT_ON_FAILURE MyConf = 1 << 7
)
// enum-iota-bitmasks/flags/main.go
The configuration is now just a single argument of type MyConf (which is an uint8 behind).
When we call our function, we just have to provide the required flags :
MyComplexFunction(VERBOSE|REBOOT_ON_FAILURE, "mysql...")
Here we tell our function that we want to activate the verbose mode and reboot on failure. The value of VERBOSE|REBOOT_ON_FAILURE is
equal to 10000001. Only two bits are set to 1, the first (VERBOSE) and the last (REBOOT_ON_FAILURE).
15 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
// enum-iota-bitmasks/flags/main.go
func main(){
MyComplexFunction(VERBOSE|REBOOT_ON_FAILURE, "test")
}
func MyComplexFunction(conf MyConf, databaseDsn string) {
fmt.Printf("conf : %08b\n", conf)
test := conf & REBOOT_ON_FAILURE
fmt.Printf("test : %08b\n", test)
}
conf : 10000001
test : 10000000
The variable test is different of 0 (00000000 as a byte). It seems to work. Let’s take another example :
test 00100000
The flag has been toggled; the test says that it is now activated.
Will output 00000000, which means that the flag has been cleared from conf.
11.0.0.1 Summary
16 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
Go is a compiled language
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
12 Test yourself
12.1 Questions
//snippet 1
type Elem uint8
const (
OptB Elem = iota + 5
OptA
OptC
)
3. What are the binary operators for AND, OR, XOR, NOT, AND NOT?
12.2 Answers
1. True or False. Go version 1 supports enum types.
1. False
2. When you want to create an enum type, you will need to write additional methods
1. 6 (1+5)
3. What are the binary operators for AND, OR, XOR, NOT, AND NOT?
1. AND : &
2. OR : |
3. XOR : ^
4. NOT : ^
17 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
1. Elem
5. What is the operator to shift to the left a binary digit ?
1. >>
13 Key takeaways
• An enumeration type (or enum) is a data type that is composed of a set of values explicitly defined
◦ Ex: The statuses of an e-commerce order (Created, Locked, Paid, Shipped, ...)
◦ E needs to implement the following interfaces json.Marshaler, json.Unmarshaler, fmt.Stringer (if you use JSON, if you use
another marshaling format in your application, you will need to implement the required interfaces).
• Its value starts at zero and is equal to the index of the constant.
• A byte is 8 bits.
• Go has bitwise logical and shift operators. We can use them only with integers
& : AND
| :OR
^ :XOR
• You can use an uint8 to pass a set of 8 boolean arguments to a function or method
◦ We can use bitwise operators to check the value of those boolean arguments.
Bibliography
• [institute1990ieee] Electrical, Institute of, and Electronics Engineers. 1990. “IEEE Standard Glossary of Software Engineering Terminology:
Approved September 28, 1990, IEEE Standards Board.” In. Inst. of Electrical; Electronics Engineers.
• [go-specs] “The Go Programming Language Specification.” n.d. Accessed April 30, 2018. https://golang.org/ref/spec.
• [go-specs] “The Go Programming Language Specification.” n.d. Accessed April 30, 2018. https://golang.org/ref/spec.
• [go-specs] “The Go Programming Language Specification.” n.d. Accessed April 30, 2018. https://golang.org/ref/spec.
18 of 19 02/01/2023, 02:15
Enum, Iota & Bitmask - Practical Go Lessons https://www.practical-go-lessons.com/chap-27-enum-iota-and-bitmask
Previous Next
Table of contents
Did you spot an error ? Want to give me feedback ? Here is the feedback page! ×
Newsletter:
Like what you read ? Subscribe to the newsletter.
@ my@email.com
Practical Go Lessons
By Maximilien Andile
Copyright (c) 2023
Follow me Contents
Posts
Book
Support the author Video Tutorial
About
The author
Legal Notice
Feedback
Buy paper or digital copy
Terms and Conditions
19 of 19 02/01/2023, 02:15