[Go]1.Constant and Enum
Intro
Go is a strongly typed and statically typed language, which will check types in compile time and doesn't allow implict typ conversion. In C we might have:
unsigned int u = 1e9;
long signed int i = -1;
... i + u ...
The result of the above code snippet will depend on the specifications of different compilers, which can be nasty to debug with. In Go if we have:
var u uint = 1e9
var i int = -1
i + u // Error
uint(i) + u // OK
i + int(u) // OK
This checks will happen on variables, but what about constants. In Go we can have untyped constants which can bypass this check.
Constant
Untyped Constant
In Go, we can use the keyword const
to define a constant. A constant is an introduced name for a value. For example:
const typedHello string = "Hello, 世界"
By specifying string
here, we expicitly say that this constant is a string. We can omit this part to define an untyped constant.
const hello = "Hello, 世界"
We can assign the typed constant value to a variable with the same type, the following behavior is allowed:
var s string
s = typedHello
fmt.Println(s)
But this is not allowed, since type alias in Go is regarded as a different type:
type MyString string
var m MyString
m = typedHello // Type error
fmt.Println(m)
By forcing a type conversion we can do the assignment:
m = MyString(typedHello)
fmt.Println(m)
Assigning the a untyped constant or a string literal is fine:
m = hello // OK
m = "Hello, 世界" // Also OK
I think an untyped constant works just the same as a string literal, so the untyped constant purely introduced a naming on a literal, no other constraints are added. And typed constants obey all the rules of typed values in Go.
Default Type
In Go, we have three different methods to declare a new variable, and two of them don't require us to specify a type:
str := "Hello, 世界"
var str = "Hello, 世界"
var str string = "Hello, 世界"
This is because that each literal value has a default type, here "Hello, 世界"
obiviously has a default type string. If we don't want to use a default type, say we want to assign 8 to an uint value, and 8's default type is int, we can choose one of the following ways:
var u uint = 8
var u = uint(8)
u := uint(8)
If a number literal's value is out of range of a specified type, Go won't do implicit conversion for you but report an error:
var i8 int8 = 128 // Error: too large.
var u8 uint8 = -1 // Error: negative value.
type Char byte
var c Char = '世' // Error: '世' has value 0x4e16, too large.
const MaxUint uint = -1 // Error: negative value
const MaxUint uint = uint(-1) // Error: negative value
const MaxUint uint = ^0 // Error: overflow
But a negative variable can be explicitly converted to an unsigned integer:
var u uint
var v = -1
u = uint(v)
Enum
Overview
We can use a group of constants to represent enum:
const (
Yellow = 0
Red = 1
Blue = 2
)
But this is hard to maintain the values. If we want to insert a value before Red
we need to change Red
and Blue
's value too. Luckily, in Go we have iota
to deal with this problem.
iota Basics
If we use iota
, the code snippet will be:
const (
Yellow = iota // 0
Red // 1
Blue // 2
)
If we want to insert a new value, we can simply add the name to the expected position:
const (
Yellow = iota // 0
Pink // 1
Red // 2
Blue // 3
)
iota
record the counts of names within a list of a const
declaration, it can only be used in const declaration. In a list of const
declaration, if a name is not assigned with a value, it will adopt the value of the last declaration. The above code snippet is equivalent to:
const (
Yellow = iota // 0
Pink = iota // 1
Red = iota // 2
Blue = iota // 3
)
iota
is not increased only if it's called, it's increased by every line of const declaration. Let's see this example:
const (
a = iota // 0
b // 1
c // 2
d = "hello" // individual value "hello", iota += 1
e // "ha", iota += 1
f = 100 // 100, iota +=1
g // 100 iota +=1
h = iota // 7
i // 8
)
Another example:
a = iota // 0
b = 10 // 10
c // 10
d, e = iota, iota // 3, 3
f = iota // 4
Remember, iota
is increased only if we start a new line, so d
and e
are both 3 in this case.
Using a different list of declaration will reset iota
:
const (
a = iota // 0
b int = iota // 1
)
const (
c = iota // 1
)
Iterate
There is no out-of-the-box method of iterate over the enum values created by a list of const
declaration in Go, but we can use this:
type Dir int
const (
NORTH Dir = iota
NORTHEAST
EAST
SOUTHEAST
SOUTH
SOUTHWEST
WEST
NORTHWEST
dirLimit // this will be the last Dir + 1
)
for dir := Dir(0); dir < dirLimit; dir++ {
// do what you want
}
Or we can create an extra array:
var Weekdays = []time.Weekday{
time.Sunday,
time.Monday,
time.Tuesday,
time.Wednesday,
time.Thursday,
time.Friday,
time.Saturday,
}
func main() {
for _, day := range Weekdays {
fmt.Println(day)
}
}
Advanced Operation
Skip Value
const (
C1 = iota + 1
_
C3
C4
)
fmt.Println(C1, C3, C4) // "1 3 4"
Use with String
type Direction int
const (
North Direction = iota
East
South
West
)
func (d Direction) String() string {
return [...]string{"North", "East", "South", "West"}[d]
}
Mixed with Computation
const (
_ = iota // 0 - int
a = iota + 10 // 11 - int
b int = iota * 10 // 20 - int
c float64 = iota + 5.1 // 8.1 - float64
)
Create Bitmask
const (
Secure = 1 << iota // 0b001
Authn // 0b010
Ready // 0b100
)