Constants and iota
Declaring compile-time constants in Go and using iota to generate related values without repeating yourself.
3 min read
Constants in Go are values fixed at compile time — they can't be reassigned, and unlike variables, their value must be knowable before the program even runs.
Declaring constants
const Pi = 3.14159
const MaxRetries = 3
const AppName = "DevLearnHub"You can group related constants in a block, which is the more idiomatic style once you have more than one or two:
const (
StatusPending = "pending"
StatusActive = "active"
StatusArchived = "archived"
)Trying to reassign a constant is a compile error, not a runtime surprise:
const MaxUsers = 100
MaxUsers = 200 // compile error: cannot assign to MaxUsersTyped vs untyped constants
A constant declared without an explicit type is untyped and takes on whatever type context demands:
const Timeout = 5
var seconds int = Timeout
var floatTimeout float64 = Timeout // works fine, Timeout adaptsThis is more flexible than a strictly typed language would allow — the same untyped constant can be used as an int in one place and a float64 in another without an explicit conversion.
iota: Go's answer to enums
Go has no enum keyword. Instead, it has iota, a counter that increments automatically within a const block, starting at 0:
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)Each line implicitly repeats the expression from the first line, so you only write iota once. This is the standard Go idiom for representing a fixed set of related options — days of the week, status codes, log levels — anywhere another language would reach for an enum.
iota with expressions
Because iota is just a number that increments, you can use it in arithmetic to generate more interesting sequences, like powers of two — a common pattern for bit flags:
const (
_ = iota // skip 0
KB = 1 << (10 * iota) // 1 << 10
MB // 1 << 20
GB // 1 << 30
)The leading _ discards the zero value entirely (a common trick when 0 isn't a meaningful option), and each subsequent constant reuses the shifted expression with its own incrementing iota.
A practical typed enum
Combining iota with a named type gives you something close to a real enum, including type safety:
type Status int
const (
Pending Status = iota
Active
Archived
)
func describe(s Status) string {
switch s {
case Pending:
return "pending"
case Active:
return "active"
case Archived:
return "archived"
default:
return "unknown"
}
}Now a function expecting a Status can't accidentally be passed a raw int or an unrelated constant — the compiler enforces it. This pattern shows up constantly in real Go codebases anywhere a fixed, closed set of values needs representing, and it's worth internalizing early since it looks unfamiliar the first time you see it.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.