Menu

Golang const: Typed and Untyped Constants Explained

How const works in Go: declaring constants, the difference between typed and untyped constants, constant expressions with arbitrary precision, and why Go has no constant slices, maps or structs.

This page includes runnable editors - edit, run, and see output instantly.

A constant is a value fixed at compile time. Declare it with const, and Go refuses any attempt to change it.

Constants can be declared at package level or inside a function, one per line or grouped in a const ( ... ) block. MB and GB are computed from other constants, and the compiler does that arithmetic once, at build time.

Assigning to a constant, such as MaxRetries = 5, is a compile error:

./main.go:8:2: cannot assign to MaxRetries (neither addressable nor a map index expression)

What Can Be a Constant

Only three kinds of values: booleans, numbers (integers, floats, complex numbers and runes) and strings. The value must be computable by the compiler, which means literals, other constants, arithmetic on them, and a few built-ins like len of a constant string.

Anything that needs to run code or allocate memory is out:

const Colors = []string{"red", "green"} // slice
const Started = time.Now()              // function call
./main.go:9:16: []string{…} (value of type []string) is not constant
./main.go:10:13: time.Now() (value of struct type time.Time) is not constant

Why Go Has No Constant Slices or Maps

Slices, maps and structs containing them are references to memory that exists at run time, so the compiler cannot bake them into the program as fixed values, and Go has no readonly or final modifier for variables. The common workarounds:

The copy costs an allocation per call, which is fine for configuration-sized data. For lookups by key, a switch in a function is another constant-like option: func statusText(code int) string { switch code { ... } }.

Untyped Constants

A constant declared without a type is untyped. It has a kind (integer, float, rune, string, bool) but no specific Go type until it is used, and it adapts to the context:

Ratio works as an int, a float64, a uint8 and a float32 without a conversion. That is why time.Sleep(2 * time.Second) compiles: 2 is untyped and becomes a time.Duration.

When the context does not demand a type, as with x := Ratio, the constant gets its default type:

Untyped constant kindDefault type
integer (42)int
floating-point (4.2)float64
rune ('a')rune (int32)
complex (2i)complex128
stringstring
booleanbool

Exact arithmetic

Untyped numeric constants are exact. The compiler represents integers with at least 256 bits, so intermediate values may be far larger than any Go type:

Huge itself cannot be printed as an integer, because passing it to fmt.Println converts it to int, and the compiler catches that (float64(Huge) works, since a float can hold the magnitude):

cannot use Huge (untyped int constant 1267650600228229401496703205376) as int value in argument to fmt.Println (overflows)

The same check stops smaller mistakes at compile time, such as var b byte = 300:

cannot use 300 (untyped int constant) as byte value in variable declaration (overflows)

Typed Constants

Give a constant a type and it behaves like a value of that type everywhere, including Go's refusal to mix types:

const Limit int = 10

var f float64 = 2
fmt.Println(Limit * f)
invalid operation: Limit * f (mismatched types int and float64)

With const Limit = 10 (untyped), Limit * f compiles and gives 20. So leave constants untyped unless the type carries meaning. It does when you declare constants of your own named type, which is how Go builds enums:

type Weekday int

const (
	Sunday Weekday = iota
	Monday
	Tuesday
)

Here the type is the point: a function taking a Weekday documents what it wants. iota and the enum patterns built on it are covered on the enums and iota page.

const vs var

constvar
Can changenoyes
Value knownat compile timeat run time
Allowed typesbool, numeric, stringany
Takes memory at run timenoyes
Can take its address (&x)noyes
Unused one is an errornoyes, for locals

Use const for anything that is truly fixed: limits, sizes, names, protocol codes, format strings. You get compile-time overflow checks and exact arithmetic for free.

Gotchas

Typed constants do not adapt. const Timeout int = 5 cannot be passed where a time.Duration is expected without a conversion; an untyped const Timeout = 5 can be multiplied: Timeout * time.Second.

Integer division happens on integer constants. const Half = 1 / 2 is 0, because both operands are untyped integers. Write 1.0 / 2 to get 0.5.

No address. &MaxRetries does not compile. If an API wants a *int, copy the constant into a variable first.

Frequently Asked Questions

How do you declare a constant in Go?

With const: const MaxRetries = 3 or, with an explicit type, const Timeout time.Duration = 5 * time.Second. Group several in a block: const ( A = 1; B = 2 ). The value must be computable at compile time.

Can I make a const slice, map or array in Go?

No. Constants can only be booleans, numbers (including runes), and strings. const Colors = []string{"red"} fails with is not constant. Use a package-level var, and if callers must not modify it, expose a function that returns a fresh copy.

What is an untyped constant in Go?

A constant declared without a type, such as const Pi = 3.14159. It has no fixed type until it is used, so the same constant works as a float32, a float64 or, if its value allows, an int. Untyped constants are also exact: the compiler keeps at least 256 bits of precision, so const Big = 1 << 100 is legal as long as you only use it in expressions whose results fit.

What is the difference between const and var in Go?

A const is fixed at compile time and can never change; it takes no memory at run time and can only hold a bool, number or string. A var is a memory location that can be reassigned and can hold any type, including values computed at run time such as time.Now().

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED