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

Video