Hello!
Let’s learn what constants are, how they should be named in golang, and also what iota is.
Naming
If you are familiar with constants in other programming languages, you know that their names usually include only capital letters, for example, PORT_NUMBER. In golang, we cannot do this because by making the first letter uppercase, we will immediately make the constant available to other packages. Therefore, in golang, we name constants in the same way as variables portNumber or PortNumber if we want the constant to be available in other packages.
package main
import "fmt"
func main() {
const portNumber int = 80
fmt.Printf("%v: %T", portNumber, portNumber)
}
$ go run main.go
80: int
Just as with the variables, the type may be omitted
const portNumber = 80
The peculiarity of the constant is that it must have a value at the time of compilation, for example, if in the constant I try to take the power of the constant value, I will get an error, because it is calculated at the time of starting the program, and not at the compilation stage
package main
import (
"fmt"
"math"
)
func main() {
const portNumber float64 = math.Pow(2.0, 2.0)
fmt.Println(portNumber)
}
$ go run main.go
# command-line-arguments
./main.go:9:29: math.Pow(2.0, 2.0) (value of type float64) is not constant
Shadowing
Just like with variables, shadowing also works with constants.
package main
import (
"fmt"
)
const portNumber int32 = 80
func main() {
const portNumber int = 8080
fmt.Printf("%v: %T\n", portNumber, portNumber)
printConstant()
}
func printConstant() {
fmt.Printf("%v: %T\n", portNumber, portNumber)
}
$ go run main.go
8080: int
80: int32
Math Operations
Mathematical operations work in the same way as variables
package main
import "fmt"
func main() {
const a int = 8080
const b int = 10
fmt.Printf("%v: %T\n", a+b, a+b)
fmt.Printf("%v: %T\n", a-b, a-b)
fmt.Printf("%v: %T\n", a*b, a*b)
fmt.Printf("%v: %T\n", a/b, a/b)
}
$ go run main.go
8090: int
8070: int
80800: int
808: int
If we try to add constants of different types, we will get an error
package main
import "fmt"
func main() {
const a int32 = 8080
const b int = 10
fmt.Printf("%v: %T\n", a+b, a+b)
}
$ go run main.go
# command-line-arguments
./main.go:9:25: invalid operation: a + b (mismatched types int32 and int)
But there is one peculiarity here if we do not specify the type in one of the constants, but simply the value 8080
, then the compiler will automatically define it as an int. Then we will create another constant of type int16 and try to add them, then we will get a successful execution. This is because the compiler at the time of compilation replaces the constant with its value in all places where it is used.
package main
import "fmt"
func main() {
const a = 8080
const b int16 = 10
fmt.Printf("%v: %T\n", a, a)
fmt.Printf("%v: %T\n", b, b)
fmt.Printf("%v: %T\n", a+b, a+b)
}
To the compiler, this code will look like this
package main
import "fmt"
func main() {
const a = 8080
const b int16 = 10
fmt.Printf("%v: %T\n", 8080, 8080)
fmt.Printf("%v: %T\n", 10, 10)
fmt.Printf("%v: %T\n", 8080+10, 8080+10)
}
And as a result, we will get int16
$ go run main.go
8080: int
10: int16
8090: int16
Iota
When creating constants, you can specify the special word iota
package main
import "fmt"
const (
a = iota
)
func main() {
fmt.Printf("%v: %T\n", a, a)
}
$ go run main.go
0: int
iota
allows you to create enum constants.
package main
import "fmt"
const (
a = iota
b = iota
c = iota
)
func main() {
fmt.Printf("%v: %T\n", a, a)
fmt.Printf("%v: %T\n", b, b)
fmt.Printf("%v: %T\n", c, c)
}
$ go run main.go
0: int
1: int
2: int
The word iota
for each constant is optional if these constants are in the same block
package main
import "fmt"
const (
a = iota
b
c
)
func main() {
fmt.Printf("%v: %T\n", a, a)
fmt.Printf("%v: %T\n", b, b)
fmt.Printf("%v: %T\n", c, c)
}
$ go run main.go
0: int
1: int
2: int
Sometimes it is necessary to omit the value in the constant
package main
import "fmt"
const (
_ = iota
a
b
c
)
func main() {
fmt.Printf("%v: %T\n", a, a)
fmt.Printf("%v: %T\n", b, b)
fmt.Printf("%v: %T\n", c, c)
}
$ go run main.go
1: int
2: int
3: int
Also, iota
can start not from zero if you add a number to it
package main
import "fmt"
const (
a = iota + 5
b
c
)
func main() {
fmt.Printf("%v: %T\n", a, a)
fmt.Printf("%v: %T\n", b, b)
fmt.Printf("%v: %T\n", c, c)
}
$ go run main.go
5: int
6: int
7: int