Constants
Before You Start
- Applies to: Go 1.21+ (examples verified with Go 1.22) · Prerequisites: Variables · Read time: about 5 min
The value of constants in Go cannot be changed at runtime. Once assigned, it cannot be modified. Its value can only come from:
- Literals
- Other constant identifiers
- Constant expressions
- Type conversions that result in constants
- iota
Constants can only be basic data types and cannot be:
- Other types besides basic types, such as structs, interfaces, slices, arrays, etc.
- Function return values
The value of constants cannot be modified, otherwise it won't compile.
Initialization
Constants are declared using the const keyword. A constant must be initialized with a value when declared, and the type of the constant can be omitted.
const name string = "Jack" // Literal
const msg = "hello world" // Literal
const num = 1 // Literal
const numExpression = (1+2+3) / 2 % 100 + num // Constant expressionIf you only declare without assigning a value, it won't compile:
const name stringThe compiler will report an error:
missing init expr for nameBatch declaration of constants can use () to improve readability. Multiple () can be used to achieve grouping.
const (
Count = 1
Name = "Jack"
)
const (
Size = 16
Len = 25
)In the same constant group, constants after an assigned constant don't need to be assigned. Their value defaults to the previous value:
const (
A = 1
B // 1
C // 1
D // 1
E // 1
)iota
iota is a built-in constant identifier, usually used to represent an untyped integer ordinal in a constant declaration. It is generally used within parentheses.
const iota = 0Let's look at some usage cases:
const (
Num = iota // 0
Num1 // 1
Num2 // 2
Num3 // 3
Num4 // 4
)Can also be written like this:
const (
Num = iota*2 // 0
Num1 // 2
Num2 // 4
Num3 // 6
Num4 // 8
)Or:
const (
Num = iota << 2*3 + 1 // 1
Num1 // 13
Num2 // 25
Num3 = iota // 3
Num4 // 4
)From the examples above, we can see that iota is incremental. The first constant using iota is assigned an expression, and based on the ordinal value changes, it will automatically assign values to subsequent constants until a new const resets it. This ordinal is actually the relative line number of the code, relative to the first line of the current group.
const (
Num = iota<<2*3 + 1 // 1 First line
Num2 = iota<<2*3 + 1 // 13 Second line
_ // 25 Third line
Num3 //37 Fourth line
Num4 = iota // 4 Fifth line
_ // 5 Sixth line
Num5 // 6 Seventh line
)The example uses the anonymous identifier _ to occupy a line. You can see that the value of iota is essentially the difference between the line where iota is located and the first line of the current const group. Different const groups don't affect each other.
Enums
Go doesn't have a separate data type for enums, unlike other languages which usually have an enum. Generally in Go, enums are implemented through custom types + const + iota. Here is a simple example:
type Season uint8
const (
Spring Season = iota
Summer
Autumn
Winter
)These enums are actually numbers. Go also doesn't support direct conversion to strings, but we can add methods to custom types to return their string representation by implementing the Stringer interface.
func (s Season) String() string {
switch s {
case Spring:
return "spring"
case Summer:
return "summer"
case Autumn:
return "autumn"
case Winter:
return "winter"
}
return ""
}This is a simple enum implementation. You can also use the official tool Stringer to automatically generate enums.
However, it has the following disadvantages:
Type unsafe, because
Seasonis a custom type, other numbers can be forcibly converted to this typegoSeason(6)Tedious, string representation needs to be implemented manually
Weak expressiveness, because
constonly supports basic data types, these enum values can only be represented with strings and numbers
Why doesn't Go support enums at the language level is something I really don't understand. I think it would definitely be more beneficial than detrimental.
Complete Example
The program below runs as-is: copy it into a file and execute go run main.go, or paste it into the Go Playground.
package main
import "fmt"
// iota: the first constant in a const block is 0, then increments per ConstSpec
const (
KB = 1 << (10 * (iota + 1))
MB
GB
)
// Typed enum-style constants with iota
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
)
func main() {
fmt.Println(KB, MB, GB)
fmt.Println(Sunday, Monday, Tuesday)
const pi = 3.14159
const greeting = "hello"
fmt.Println(pi, greeting)
}Expected output:
1024 1048576 1073741824
0 1 2
3.14159 helloCommon Pitfalls
- Constants cannot be assigned to or take an address.
const c = 1; c = 2and&care both compile errors. iotarestarts in everyconstblock and only advances per ConstSpec. Use_ = iotato skip values.- Parenthesize bit-shift combinations with
iota. Writing1 << 10 * (iota + 1)groups as(1 << 10) * (iota + 1), which is not what most people expect. - Only basic types can be constants. Slices, maps, structs and arrays cannot be
const; use package-levelvarinstead. - Untyped constants take type from context.
const x = 1is untyped and can be used asintorfloat64; once you add an explicit type, conversions become mandatory.
Change Log
- 2026-10-14: Added "Before You Start" notes, a complete runnable example with expected output, common pitfalls, and this change log.
