Iota Behavior

So, I bumped into a weird behavior with iota in Go that I wasn’t expecting, thought I’d document it here so that I’d find my own blog post next time I searched on it. (Isn’t that how it always works?)

package main

import (
	"fmt"
)

const (
	STUFF = "STUFF"

	a0 = iota
	a1
	a2
)

func main() {
	fmt.Printf("STUFF=%v, a0=%d, a1=%d, a2=%d",STUFF, a0, a1, a2)
}

With the above code, what is the value of a1?

If you said 1, you would be wrong. The value of a1 is 2 because, the counting begins from the reserved word const. So, STUFF is 0. a0 is 1. a1 is 2.

In my head, the moment you declare iota, you should be starting at zero. After a lively discussion on twitter, it turns out that I’m reading the spec wrong:

Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants. It is reset to 0 whenever the reserved word const appears in the source and increments after each ConstSpec.

This particular section is important It is reset to 0 whenever the reserved word const appears.

The code is easy enough to fix despite looking a little unorganized / clunky.

package main

import (
	"fmt"
)

const (
	STUFF = "STUFF"
)

const (
	a0 = iota
	a1
	a2
)

func main() {
	fmt.Printf("STUFF=%v, a0=%d, a1=%d, a2=%d", STUFF, a0, a1, a2)
}

I highly recommend when working with iota creating new blocks for the iota set. Of course, none of this is really an issue unless you’re persisting that iota in a datastore.

Let’s say you had a roles iota like this:

const (
someOtherConstant = "foobar"

member = iota
moderator
admin
)

You save the iota of a member (1) / moderator (2) / admin (3) in a database. Some new feature comes along and now you need to add a new constant:

const (
someOtherConstant = "foobar"
newConstant = "Really foobar'ed"

member = iota
moderator
admin
)

Now you’ve introduced a bug in your code because the values have shifted ( member (2) / moderator (3) / admin (4) ) and you’re no longer in sync with the database.

So, when you’re working with iota double check your application’s behavior. If you’re not persisting, just make sure that you’re getting the expected behavior in your code.

Luckily, I wasn’t persisting and I stumbled across this behavior in a go test. :)