Go supports constants of character, string, boolean, and numeric values.
const declares a constant value.
const s string = "constant"A const statement can appear anywhere a var statement can.
const n = 500000000Constant expressions perform arithmetic with arbitrary precision.
const d = 3e20 / n
fmt.Println(d)A numeric constant has no type until it's given one, such as by an explicit conversion.
fmt.Println(int64(d))Division on untyped constants keeps full precision until a type is forced.
$ go run constants.go
6e+11
600000000000Constants can't hold runtime values
const works for the environment variable's name, a string known at compile time. The key itself isn't: os.Getenv reads it from the process environment at runtime, a function call, so it has to be a var.
const apiKeyEnv = "API_KEY"
apiKey := os.Getenv(apiKeyEnv)
if apiKey == "" {
log.Fatal("API_KEY not set")
}The key never appears in the program's source or its output, only its length. Logging a real secret, even for debugging, is how it ends up somewhere it shouldn't.
$ API_KEY=sk-demo-12345 go run constants.go
loaded a 13-character keyiota: Go's enumerated constants
Go has no dedicated enum keyword. iota is the idiomatic replacement: inside a const block it starts at 0 and increments by one for every line, so Sunday through Saturday get consecutive values without writing them out.
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)Weekday is just int underneath. Printing a value straight from the const block prints that underlying number, not a name.
fmt.Println(Wednesday)
fmt.Println(int(Wednesday))Wednesday is the fourth name in the block, so its iota value is 3. int(Wednesday) makes the conversion explicit; both lines print the same number either way.
3
3A leading _ = iota discards the 0 value without using it. iota keeps counting on skipped lines too, so KB starts from 1, and the same expression, evaluated with iota at 1, 2, and 3, produces each size a thousand times the last.
const (
_ = iota // skip 0
KB = 1 << (10 * iota)
MB
GB
)
fmt.Println(KB, MB, GB)1024, 1024², and 1024³, the standard binary byte-size ladder, generated from one expression instead of three separate literals.
1024 1048576 10737418241 << iota is the other common pattern: each constant becomes the next power of two, so every value occupies its own bit. Combining them with | builds a set of flags; checking one with & tests whether that bit is set.
type Permission int
const (
Read Permission = 1 << iota
Write
Execute
)
perms := Read | Execute
fmt.Println(perms&Write != 0)
fmt.Println(perms&Read != 0)perms only has Read and Execute set. Masking with &Write comes back zero; masking with &Read doesn't, since that bit is actually set.
false
trueA type implementing String() string satisfies fmt.Stringer, and every fmt print function checks for it automatically. Attaching one to Weekday turns the same underlying integer into readable output everywhere it gets printed, no call site changes needed.
func (d Weekday) String() string {
names := [...]string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
return names[d]
}
fmt.Println(Wednesday) // fmt calls String() automaticallySame value, same fmt.Println(Wednesday) call as before, now printing Wednesday instead of 3, purely because the type grew a String method.
Wednesday