In a Go const block, how does iota repeat an omitted expression, and does _ still advance it?
answer
- the shorthand copies something upward
- copied text, fresh line index
- blank identifier is still a line
- comments and blank lines do not count
- it repeats the most recent expression
basics
~20 sInside a parenthesized const block, a line with no expression repeats the previous line's expression, with iota re-evaluated for it. A line declaring only the blank identifier still counts, so it consumes an iota value.
solid answer
~50 sIn a parenthesized `const` declaration, a line that omits its expression list repeats the previous line's expression *textually*, and `iota` is re-evaluated for the new line. That is what makes the size-table idiom work: write `1 << (10 * iota)` once and the following bare names become 1<<20, 1<<30 and so on. The repetition follows the most recent non-empty expression, so if a later line writes a new expression, subsequent bare lines repeat *that* one — including a constant with no `iota` in it, which then yields the same value repeatedly. The blank identifier is a normal ConstSpec: a line reading `_ = iota` declares nothing but still occupies a line index, which is exactly how you skip an unwanted value such as the zero in a size table. Comments and blank lines are not lines in this sense and never advance the counter.
code
go · 8 linesconst (
_ = iota
KB = 1 << (10 * iota)
MB
GB
)
// KB == 1<<10, MB == 1<<20, GB == 1<<30.go deeper
Recognise the shorthand when you read it: bare names under an expression are not missing anything, they reuse the line above with a new counter value.
Explain that the repetition is textual and re-evaluated per line, and that the blank identifier occupies a line index, which is how the size-table idiom skips zero.
Judge readability in review: a block mixing an iota expression with literals makes bare lines inherit the wrong rule, and retiring a value with the blank identifier is safer than deleting the line.
Decide what the team's convention is for numbered constants that outlive a release, including whether a retired number may ever be reused by a later constant.
### The rule Inside a parenthesized `const` declaration, if a ConstSpec omits its expression list, the list is taken to be *the same as the preceding non-empty one*, repeated textually. Because it is a textual repetition, and because `iota` is re-evaluated as the index of the line where the repetition lands, each repeated line can produce a different value. ```go const ( _ = iota KB = 1 << (10 * iota) MB GB ) ``` Line 0 is `_ = iota` — it declares nothing but occupies index 0. Line 1 gives `KB = 1 << 10`. Lines 2 and 3 repeat `1 << (10 * iota)` with `iota` equal to 2 and 3, producing 1<<20 and 1<<30. Written out longhand it is four ordinary const specs; the repetition is only shorthand. ### The blank identifier is a real line `_` is the blank identifier, and a spec that declares only `_` is still a spec. That is precisely why it is used here: it burns index 0 so the table starts at KB rather than at a useless `1 << 0`. The same trick skips a value in the middle of an enum when a number has been retired and must not be reused: ```go const ( StatusOpen Status = iota _ // 1 is retired; never reuse it StatusClosed // 2 ) ``` Contrast that with things that are *not* specs: a comment line, a blank source line, or a `//go:` directive. None of them advance `iota`, which is why formatting changes are safe and why an inserted *declaration* is not. ### Repetition follows the last expression written, not the first This is the part people get wrong. The repeated text is the most recent non-empty expression list, so a new expression halfway down the block changes what the following bare lines mean: ```go const ( a = iota // 0 b // 1 c = 5 // 5 d // 5, not 3 ) ``` `d` repeats `5`, an expression that contains no `iota` at all, so it is 5 as well — while `iota` quietly equals 3 on that line and is simply unused. A const block that mixes an `iota` expression with a literal one is therefore much harder to read than it looks, and reviewers should push back on it. ### Several names on one line The repetition applies to the whole expression list, and `iota` is fixed for the line, so parallel columns work: ```go const ( KB, KiB = 1000, 1 << 10 ) ``` That declares two names from one spec. If you extend the pattern with bare lines, each bare line repeats *both* expressions and advances `iota` once for the pair — never once per name. ### Offsets, and where this idiom belongs Because `iota` is an ordinary term in a constant expression, all of these are the same mechanism: `iota + 1` to start at one, `1 << iota` for bit flags, `10 * iota` for a stepped scale. There is no special syntax; the compiler substitutes a number and evaluates a constant expression exactly. The repetition shorthand is at its best when every line genuinely means "same rule, next index" — a size table, a flag set, a plain enum. It is at its worst when a reader must scan upward to work out which expression a bare name inherits. If the block needs a comment to explain what a bare line evaluates to, write the expressions out. ### What to say in an interview State the rule (omitted expression repeats the previous one, `iota` re-evaluated per line), demonstrate it with the shifted size table, then add the two details that separate a confident answer from a memorised one: `_` occupies a line and therefore skips a value, and repetition follows the most recently written expression rather than the top of the block.
- Does adding a comment line in the middle of an iota const block change the values below it?No. Only ConstSpecs advance `iota`; comments, blank source lines and directives are not specs. Adding or reflowing comments is safe. Adding a *declaration* — even one that declares only the blank identifier — shifts every value below it.
- Why do teams write `_ = iota` as the first line of a const block?To burn index 0 so no useful constant gets it. In a size table `1 << (10 * 0)` is 1, which is meaningless; in an enum, giving 0 to a real status means an unset field silently reads as that status. Skipping zero forces the value to be set explicitly.
- How do you retire a value in the middle of an iota block without renumbering the rest?Replace the retired constant's name with `_`, leaving the line in place. The blank spec keeps the line index occupied, so every constant below it keeps its number, and the retired value is never handed to a new name.
saying these in an interview costs you the question
- Thinks a bare line repeats the block's first expression
- Believes a blank identifier line is skipped by iota
- Says comments or blank lines advance the counter
- Expects iota to increment once per name on a line
- Assumes repetition copies the value rather than the expression