💻

iota 出現時の値はゼロとは限らない

2 min read

今回も小ネタ。

第16回『プログラミング言語Go』オンライン読書会」の『プログラミング言語Go』の 3.6.1章「定数生成器 iota」で出た話で,書籍には

const 宣言では、 iota の値はゼロから始まり、順番に個々の項目ごとに1増加します。

とあるが,ここで翻訳者であり読書会の主宰である柴田芳樹さんの解説があった。今回はその話。

元々 const では

package main

import "fmt"

const (
    one = 1
    two
    three
    four
)

func main() {
    fmt.Println(one, two, three, four)
    // Output:
    // 1 1 1 1
}

と書くと直前の定数と同じ値がセットされるという特徴がある。この性質と定数生成器 iota を組み合わせることで

package main

import "fmt"

const (
    one = 1 + iota
    two
    three
    four
)

func main() {
    fmt.Println(one, two, three, four)
    // Output:
    // 1 2 3 4
}

ひとつづつインクリメントした値をセットすることができる。じゃあ iota の初期値は常にゼロなのかというと,そこは微妙で,たとえば

package main

import "fmt"

const (
    zero = "0"
    one  = 1
    two
    three
    four = iota
)

func main() {
    fmt.Println(zero, one, two, three, four)
    // Output:
    // 0 1 1 1 4
}

てな風に書くと iota 出現時の値は 4 になる。つまり iota は出現する前から(見かけ上[1])カウントしているわけだ。

iota 出現時の値が常にゼロだと思いこんで,うっかり

package main

import "fmt"

const (
    one = 1 + iota
    two
    three
    four
    zero = iota
)

func main() {
    fmt.Println(zero, one, two, three, four)
    // Output:
    // 4 1 2 3 4
}

てなコードを書くと zero がゼロにならず「とひょーん」となってしまう。恥ずかしい話だが,実は昔このパターンでハマったことがあるのだ(テストが通らず,しばらく悩んだ)。

これを回避するには

package main

import "fmt"

const (
    one = 1 + iota
    two
    three
    four
)

const (
    zero = iota
)

func main() {
    fmt.Println(zero, one, two, three, four)
    // Output:
    // 0 1 2 3 4
}

という感じに iota 毎に別の const 宣言で括ってやればよい。

教訓: iota は(別系統の定数と)混ぜるな,危険

脚注
  1. 正しくは iota はカウンタではない。この辺の話については拙文「定数生成器 iota についてちゃんと書く」で纒めてみた。 ↩︎

GitHubで編集を提案

Discussion

ログインするとコメントできます