Introduction
Constants can be said to exist in every code file. There are many advantages to using constants:
- Avoid magic literals, that is, numbers, strings, etc. that appear directly in the code. When you read the code, you can't see its meaning at a glance. In addition, it can avoid the inconsistency that may occur when using literals. When their values need to be modified, the constants only need to be modified in one place, and the literals need to be modified in many places, which is easy to miss and cause inconsistencies;
- Relative to variables, constants can perform compile-time optimization.
Go language also provides constant syntax support, which is basically the same as the constants provided by other languages. But there are several useful features of constants in Go that are worth knowing about.
Constant basis
const
keyword to define constants in the Go language:
package main
import "fmt"
const PI float64 = 3.1415926
const MaxAge int = 150
const Greeting string = "hello world"
func main() {
fmt.Println(PI)
fmt.Println(MaxAge)
fmt.Println(Greeting)
}
Multiple constant definitions can be combined together. For example, several constant definitions above can be written in the following form:
const (
PI float64 = 3.1415926
MaxAge int = 150
Greeting string = "hello world"
)
However, it is generally recommended to define related constants of the same type in a group.
Constants in the Go language have a big limitation: only basic types of constants can be defined, namely Boolean types ( bool
), integers (unsigned uint/uint8/uint16/uint32/uint64/uintptr
, signed int/int8/int16/int32/int64
), floating-point numbers (single-precision float32
, double-precision float64
), or The underlying types are the types of these basic types. You cannot define constants of these types such as slices, arrays, pointers, and structures. For example, byte
underlying type uint8
, rune
underlying type int32
, see Go source builtin.go
:
// src/builtin/builtin.go
type byte = uint8
type rune = int32
Therefore, the class can be defined as a constant byte
or rune
const b byte = 128
const r rune = 'c'
Defining other types of variables will report an error at compile time:
type User struct {
Name string
Age int
}
const u User = User{} // invalid const type User
var i int = 1
const p *int = &i // invalid const type *int
iota
Constant definitions in Go language code often use iota
. Let's look at some Go source codes.
Standard library time
source code:
// src/time/time.go
type Month int
const (
January Month = 1 + iota
February
March
April
May
June
July
August
September
October
November
December
)
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
Standard library net/http
source code:
// src/net/http/server.go
type ConnState int
const (
StateNew ConnState = iota
StateActive
StateIdle
StateHijacked
StateClosed
)
iota
is a mechanism for us to define constants. Simply put, iota
independently in each constant definition group (each const
statement that appears separately is counted as a group), iota
appears in the constant expression used to initialize the constant value, and the value of iota
The first few lines in the constant group (starting from 0) . iota
. In this case, the type and initialization expression of the previous definition will be used. Let's look at a few sets of examples:
const (
One int = iota + 1
Two
Three
Four
Five
)
This is also the most commonly used method, where iota
appears in the first row, and what is its value. In the above constant definition group, One
in line 0 (note that the counting starts from 0), iota
is 0, so One = 0 + 1 = 1
.
The next line Two
omits the type and initialization expression, so Two
uses the above type int
, and the initialization expression is also iota + 1
. But this is the first row in the definition group, and iota
is 1, so Two = 1 + 1 = 2
.
The next line Three
also omits the type and initialization expression, so Three
follows Two
and then the One
type int
. The initialization expression is also iota + 1
, but this is the second line of the definition, so Three = 2 + 1 = 3
. And so on.
iota
in very complex initialization expressions:
const (
Mask1 int = 1<<(iota+1) - 1
Mask2
Mask3
Mask4
)
According to the above analysis, Mask1~4
is 1, 3, 7, 15 in order.
There are also odd and even numbers:
const (
Odd1 = 2*iota + 1
Odd2
Odd3
)
const (
Even1 = 2 * (iota + 1)
Even2
Even3
)
In a group, iota
does not necessarily appear in the 0th row, but it appears in the first few rows, the value is :
const (
A int = 1
B int = 2
C int = iota + 1
D
E
)
The above iota
appears in the second row (starting with 0), C
value 2 + 1 = 3
. D
and E
are 4 and 5, respectively.
must be noted that iota
is equal to the row where it appears in the group, not the number of times it appears.
You can ignore the value by assigning to the empty identifier:
const (
_ int = iota
A // 1
B // 2
C // 3
D // 4
E // 5
)
Having said so many iota
, why use iota
? In other words, what are the advantages of iota
I think there are two points:
- It is convenient to define. When the pattern is relatively fixed, we can only write the first one, and the type and initialization expression of the subsequent constants do not need to be written;
- It is convenient to adjust the order, add and delete constant definitions. If we want to adjust the order after defining a set of constants, use
iota
, only need to adjust the position, no need to modify the initialization formula, because there is no writing. Adding and deleting are also the same. If we write out the initialization formulas one by one and delete the middle one, the subsequent values must be adjusted.
For example, the source code in net/http
type ConnState int
const (
StateNew ConnState = iota
StateActive
StateIdle
StateHijacked
StateClosed
)
If we need to add a constant, it means the state is being closed. Now only need to write the new state name:
type ConnState int
const (
StateNew ConnState = iota
StateActive
StateIdle
StateHijacked
StateClosing // 新增的状态
StateClosed
)
If it is explicitly written out the initialization formula:
type ConnState int
const (
StateNew ConnState = 0
StateActive ConnState = 1
StateIdle ConnState = 2
StateHijacked ConnState = 3
StateClosed ConnState = 4
)
At this time, the new value needs to be changed. In addition, there are a lot more characters to type 😊:
const (
StateNew ConnState = 0
StateActive ConnState = 1
StateIdle ConnState = 2
StateHijacked ConnState = 3
StateClosing ConnState = 4
StateClosed ConnState = 5
)
Untyped constant
There is a special kind of constant in the Go language, that is, an untyped constant. That is, when defining, we do not explicitly specify the type. Such constants can store values beyond the range of conventional types:
package main
import (
"fmt"
"math"
"reflect"
)
const (
Integer1 = 1000
Integer2 = math.MaxUint64 + 1
Float1 = 1.23
Float2 = 1e100
Float3 = 1e400
)
func main() {
fmt.Println("integer1=", Integer1, "type", reflect.TypeOf(Integer1).Name())
// 编译错误
// fmt.Println("integer2=", Integer2, "type", reflect.TypeOf(Integer2).Name())
fmt.Println("integer2/10=", Integer2/10, "type", reflect.TypeOf(Integer2/10).Name())
fmt.Println("float1=", Float1, "type", reflect.TypeOf(Float1).Name())
fmt.Println("float2=", Float2, "type", reflect.TypeOf(Float2).Name())
// 编译错误
// fmt.Println("float3=", Float3, "type", reflect.TypeOf(Float3).Name())
fmt.Println("float3/float2=", Float3/Float2, "type", reflect.TypeOf(Float3/Float2).Name())
}
Although untyped constants can store values beyond the range of normal types and can perform arithmetic operations with each other, they still need to be converted back to normal types when they are used (assigned to variables and passed as parameters). If the value exceeds the range of the normal type, the compilation will report an error. Every untyped constant has a default type. The default type for integers is int
, and the default type for floating-point numbers (with decimal points or scientific notation is regarded as floating-point numbers) is float64
. So in the above example, we define Integer2
as an untyped constant, the value is uint64
+ 1, which is allowed. But if we directly output Integer2
, it will cause a compilation error, because Integer2
will be converted to int
default, and its stored value exceeds the range of int
On the other hand, we can use Integer2
for calculations, such as dividing by 10, the value obtained is in the int
, which can be output. (I am using a 64-bit machine)
The following floating-point number types are similar. Float3
exceeds the float64
, so it cannot be output directly. However Float3/Float2
result in float64
range, may be used.
The output of the above program:
integer1= 1000 type int
integer2/10= 1844674407370955161 type int
float1= 1.23 type float64
float2= 1e+100 type float64
float3/float2= 1e+300 type float64
It can also be seen from the output that the default types of integer and floating point are int
and float64
respectively.
Combining iota
and untyped constants we can define a set of storage units:
package main
import "fmt"
const (
_ = iota
KB = 1 << (10 * iota)
MB // 2 ^ 20
GB // 2 ^ 30
TB // 2 ^ 40
PB // 2 ^ 50
EB // 2 ^ 60
ZB // 2 ^ 70,1180591620717411303424
YB // 2 ^ 80
)
func main() {
fmt.Println(YB / ZB)
fmt.Println("1180591620717411303424 B = ", 1180591620717411303424/ZB, "ZB")
}
ZB
actually reached 1180591620717411303424, which exceeds the int
, but we can still define ZB
and YB
, and also perform operations on them during use, as long as the final value to be used is within the range of the normal type.
to sum up
This article introduces the knowledge of constants, just remember two points:
iota
is equal to the number of lines in the constant definition group (starting from 0);- Untyped constants can define values that exceed the storage range, but they must be able to return to the range of normal types when used, otherwise an error will be reported.
Using untyped constants, we can perform arithmetic operations on large numbers at compile time.
reference
- "Go Programming Language"
- Go GitHub you don't know: https://github.com/darjun/you-dont-know-go
I
My blog: https://darjun.github.io
Welcome to follow my WeChat public account [GoUpUp], learn together and make progress together~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。