Go by Example

Constants

const, iota, typed vs untyped constants, and constant expressions evaluated at compile time.

Constants in Go are values computed at compile time. They can be boolean, numeric, or string. Unlike variables, constants cannot be assigned to at runtime - the compiler enforces this.

const declares a single value or a block of values. Constants must be assignable from a literal or a constant expression.

package main
 
import "fmt"
 
const Pi = 3.14159
const MaxRetries = 3
const ServiceName = "auth-service"
 
func main() {
    fmt.Println(Pi, MaxRetries, ServiceName)
 
    const localTimeout = 30 // constants can be declared inside functions too
    fmt.Println(localTimeout)
}

iota is a compile-time counter that resets to 0 at the start of each const block and increments by 1 for each constant. It is the idiomatic way to define enumerations in Go.

package main
 
import "fmt"
 
type Direction int
 
const (
    North Direction = iota // 0
    East                   // 1
    South                  // 2
    West                   // 3
)
 
type ByteSize float64
 
const (
    _           = iota             // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota) // 1024
    MB                             // 1048576
    GB                             // 1073741824
)
 
func main() {
    fmt.Println(North, East, South, West)
    fmt.Println(KB, MB, GB)
}

An untyped constant has no explicit type - it adopts a default type at the point of use. A typed constant carries its type and will only satisfy type-safe comparisons.

package main
 
import "fmt"
 
const untypedMax = 1000        // untyped int constant
const typedMax int32 = 1000    // typed int32 constant
 
func printInt64(n int64) {
    fmt.Println(n)
}
 
func main() {
    printInt64(untypedMax) // OK - untyped constant is widened to int64
    // printInt64(typedMax) // compile error: cannot use typedMax (int32) as int64
    _ = typedMax
}

In production

Untyped integer constants are assigned a default type (int) at the point of use - passing one across a package boundary can produce surprising implicit widening or narrowing depending on the target platform's int size (32 vs 64 bits). Typed constants make intent explicit and prevent cross-package confusion. This matters especially for constants used as sizes, capacities, or enum discriminants that flow into serialization formats or database columns where the width is load-bearing.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free