Constants and iota

A constant is a value fixed when the program is compiled: a tax rate, a maximum size, the name of an environment variable. Go's constants have a few special powers — they can be untyped, they're calculated with exact precision, and the iota counter makes it easy to define a numbered set of related values, Go's equivalent of an enum.

Declaring constants

GO
package main

import "fmt"

const appName = "Shopfront"

const (
	maxItems = 50
	vatRate  = 0.2
	welcome  = "Welcome to " + appName // built from other constants
)

func main() {
	const discount = 5 // constants can be local too
	price := 40.0
	fmt.Println(welcome)
	fmt.Println(maxItems, price*(1+vatRate)-discount)
}
Output
Welcome to Shopfront
50 43

Constants can be numbers, strings, booleans or runes — not slices, maps or structs. Their value must be computable at compile time, so const now = time.Now() is an error. A constant can't be changed: assigning to one doesn't compile.

Typed and untyped constants

A constant declared without a type, such as const limit = 10, is untyped. It takes on whatever type its context needs, which is why one constant can be used with several numeric types:

GO
package main

import "fmt"

const limit = 10          // untyped
const typedLimit int = 10 // typed: always an int

func main() {
	var a int8 = limit
	var b float64 = limit
	var c uint = limit
	fmt.Println(a, b, c)

	var d float64 = float64(typedLimit) // a typed constant needs a conversion
	fmt.Println(d)
}
Output
10 10 10
10

An untyped constant still gets a default type when there's no context, for example in x := limit: int for whole numbers, float64 for decimals, rune for characters and string for text.

Exact arithmetic

The compiler calculates constant expressions with very high precision, and only rounds when a constant is finally given a concrete type. Intermediate results can be far larger than any integer type:

GO
package main

import "fmt"

const huge = 1 << 100       // far beyond int64
const backDown = huge >> 98 // 4: fits comfortably

func main() {
	fmt.Println(backDown)
	fmt.Println(0.1+0.2 == 0.3) // exact at compile time
}
Output
4
true

Only the final value has to fit its type. fmt.Println(huge) would not compile, because huge overflows int.

iota

Inside a const ( … ) group, the identifier iota stands for the line's position: 0 for the first constant, 1 for the second, and so on. If a constant has no expression, it repeats the previous one — with the new iota value. Together these make enumerations easy:

GO
package main

import "fmt"

type Weekday int

const (
	Sunday  Weekday = iota // 0
	Monday                 // 1
	Tuesday                // 2
	Wednesday
	Thursday
	Friday
	Saturday
)

func main() {
	fmt.Println(Sunday, Wednesday, Saturday)
	today := Friday
	fmt.Println(today == Friday, today > Monday)
}
Output
0 3 6
true true

Giving the constants their own type, Weekday, means a function that takes a Weekday documents what it expects. In lesson 42 you'll give the type a String method so it prints as "Friday" instead of 5.

iota patterns

GO
package main

import "fmt"

type Size int

const (
	_          = iota // skip zero, so the zero value means "not set"
	Small Size = iota
	Medium
	Large
)

const (
	_  = iota
	KB = 1 << (10 * iota) // 1 << 10
	MB                    // 1 << 20
	GB                    // 1 << 30
)

type Permission uint8

const (
	Read    Permission = 1 << iota // 1
	Write                          // 2
	Execute                        // 4
)

func main() {
	fmt.Println(Small, Medium, Large)
	fmt.Println(KB, MB, GB)

	p := Read | Write
	fmt.Println(p, p&Write != 0, p&Execute != 0)
}
Output
1 2 3
1024 1048576 1073741824
3 true false

Starting enums at 1 (by skipping 0 with _) is a common trick, so that a forgotten field — which defaults to zero — can be told apart from a real choice. The 1 << iota pattern makes bit flags that can be combined with | and tested with & (lesson 11).

Try it yourself

Define order statuses with iota and print one. Then add a new status in the middle and see the numbers shift.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
status code: 3
done? false
Next: Go's operators.