Last Updated: May 22, 2026
iota is a built-in identifier Go provides specifically for const blocks. It generates a sequence of integer values automatically, which makes it the standard tool for defining enum-like sets of related constants. It appears across the standard library in log levels, file modes, sort flags, and anywhere Go code needs a fixed set of named values.
iota is a predeclared identifier that resets to 0 at the start of every const block and increments by 1 for each ConstSpec line in that block. A ConstSpec is just one line of constant declaration inside const ( ... ). The first line gets iota == 0, the second line gets iota == 1, and so on.
That's the entire mechanic. There's nothing dynamic at runtime. iota is evaluated by the compiler when it processes the const block, and the constants end up as fixed integer values in the compiled binary.
Cost: iota expressions are resolved at compile time. The resulting constants have zero runtime cost, no allocation, no lookup, no function call. They're as cheap as writing the integer literal yourself.
Writing = iota on every line gets repetitive fast, which is why Go has a second rule that makes iota actually pleasant to use.
If a ConstSpec inside a const block omits its expression, Go repeats the previous expression for it. This applies to any constant declaration, but it's what makes iota patterns look clean.
Only the first line has = iota written out. The other three lines inherit the expression iota, but each one is on a new ConstSpec, so iota evaluates to a different value on each line. That's the combination that makes the pattern work: implicit repeat copies the expression, and iota changes its value per line.
Consider the lifecycle of an online order. It starts as Pending, gets Confirmed, moves to Shipped, ends as Delivered, or it could be Cancelled at any point. Each state is just a number internally, but the code is much clearer when those numbers have names.
The constants are just 0, 1, 2, 3, 4, but you never have to remember which number means what. The compiler does the bookkeeping. If you decide later to insert a new status (say, StatusPacked between Confirmed and Shipped), every value after it shifts automatically. You change one line, the rest follow.
The diagram shows what the compiler does line by line. Each ConstSpec gets a fresh value of iota, and the implicit-repeat rule copies the iota expression down from line 1.
The constants above are untyped integers, which means anyone can mix them with plain numbers. That's not great when you want the compiler to catch accidental misuse. The idiomatic Go fix is to declare a named type and use it inside the const block.
Two things changed. First, Pending OrderStatus = iota declares the first constant with an explicit type. The implicit-repeat rule carries both the type and the expression down, so Confirmed, Shipped, Delivered, and Cancelled are also OrderStatus values. Second, describe takes an OrderStatus parameter, so the compiler refuses to call it with a plain int. That extra safety is essentially free since iota already gave you the values.
You can also give each constant a readable name when you print it, by attaching a method to the type. Here's the idea in one line:
With this method in place, fmt.Println(Shipped) prints Shipped instead of 2. A typed iota constant can carry its own name into formatted output.
Sometimes you want to skip a position in the sequence. Maybe you're matching an external numbering scheme that starts at 1, or you want a slot reserved without giving it a name. The blank identifier _ lets you consume a value of iota without binding it to anything.
The _ line still counts as a ConstSpec, so iota is 0 there and increments normally on the lines below. The benefit is that the zero value of ShippingMethod (which any uninitialized variable starts with) doesn't accidentally match a real method. An unset shipping method stays clearly unset.
You can skip more than one position too. Each _ line just burns one value:
This pattern shows up in real code when you want constants to start at a specific non-zero value or when you're aligning with values defined elsewhere.
Every const ( ... ) block has its own iota that starts at 0. Two different const blocks in the same file are completely independent.
Card and Clothing both end up as 1, but the type system keeps them apart. You can't pass a PaymentMethod to something expecting a ProductCategory. Each block reset iota to 0 and counted from there independently. This is the normal way to declare multiple unrelated enums in the same file.
iota is just an integer that the compiler swaps in, so you can use it inside any constant expression. The most common pattern is 1 << iota, which produces a sequence of powers of two: 1, 2, 4, 8, 16. Powers of two are how you build bitmask flags, where each value occupies a single bit and you can combine flags with | (OR).
Consider user permission flags on a product page. A user might be able to read product info, write reviews, or do both. With bitmasks, you can represent any combination of these as a single integer.
Walking through it: ReadOnly = 1 << 0 = 1. The implicit-repeat rule carries 1 << iota down to each line, but iota changes, so WriteOnly = 1 << 1 = 2, Admin = 1 << 2 = 4, Owner = 1 << 3 = 8. Combining them with | sets the matching bits, and & (AND) tests whether a specific bit is set.
Cost: Bit operations (<<, |, &) are single CPU instructions. Checking a flag with p&Admin != 0 is faster than any map or slice lookup, and the storage cost is one byte (for uint8) regardless of how many flags you set.
This pattern shows up across the standard library: os.O_RDONLY, os.O_WRONLY, os.O_CREATE are flags for opening files and you combine them with |. regexp flags, network flags, file mode bits, all use the same technique.
iota works in any constant expression, not just shifts. You can do offsets, multiplications, or any combination, as long as the result is a constant the compiler can evaluate.
The first block uses iota * 5 to step by fives. The second block uses 1000 + iota to start counting from 1000. Whatever expression you put on the first line, the implicit-repeat rule copies it down and iota changes per line. The conversion float64(GoldDiscount) is needed only because we're mixing it with a float64 price; the constants themselves are integers.
A few things iota cannot do:
const block. Writing var i = iota in a function body is a compile error.iota + os.Getpid() is invalid because os.Getpid() isn't a compile-time constant.0, 1, 2, ... per ConstSpec, in order. To skip values, use _ lines.Use iota for a fixed set of related values the compiler tracks: enum states, bit flags, priorities, levels. Avoid it when the values aren't sequential or when their meaning comes from external systems (HTTP status codes, error codes from a third-party API). In those cases, write the numbers out explicitly so the connection to the external source is clear.
If your "enum" has gaps or non-sequential values dictated from outside, prefer explicit numbers. iota works well when the numbering is your decision to make.