0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go で Iota 識別子を使用して列挙 (enum) 的な実装をする

Posted at

Go には列挙型が存在しない

Java などに存在する列挙型ですが、Go にはそのような仕様がありません。
Go では列挙的な実装をしたいときに Iota 識別子が使用できるようです。
今回はシンプルなタスクのステータスを列挙する実装を例に Iota 識別子を使用する方法を考えます。

Iota 識別子

Go には Iota (イオタと読むらしい) という識別子があります。

言語仕様

It can be used to construct a set of related constants

言語仕様にもこのように説明があるので、列挙的な実装をしたいときに使うことが想定されていると受け取れます。

Iota 識別子を使用した列挙的な実装

どのような実装が可能なのか、いくつか試してみます。

定数に int 型の値を設定する

実装

main.go
package main

type TaskStatus int

const (
	notStarted TaskStatus = iota
	inProgress
	done
)

func main() {
	println("notStarted: ", notStarted)
	println("inProgress: ", inProgress)
	println("done: ", done)
}

実行結果

go run ./main.go
notStarted:  0
inProgress:  1
done:  2

0を最初としてインクリメントされた整数が定数の値として設定されていることが分かります。

定数に設定する値を0以外の任意の整数にする

実装

先ほどのコードに少し変更を加えます。

main.go
package main

type TaskStatus int

const (
	notStarted TaskStatus = iota + 1
	inProgress
	done
)

func main() {
	println("notStarted: ", notStarted)
	println("inProgress: ", inProgress)
	println("done: ", done)
}

実行結果

go run ./main.go
notStarted:  1
inProgress:  2
done:  3

先ほどとは異なり、1を最初としてインクリメントされた整数が定数の値として設定されていることが分かります。

定数に float64 型の値を設定する

float64 の値を設定することもできるようです。

実装

main.go
package main

type TaskStatus float64

const (
	notStarted TaskStatus = iota
	inProgress
	done
)

func main() {
	println("notStarted: ", notStarted)
	println("inProgress: ", inProgress)
	println("done: ", done)
}

実行結果

go run ./main.go
notStarted:  +0.000000e+000
inProgress:  +1.000000e+000
done:  +2.000000e+000

int 型の値を設定したとき同様、インクリメントされた値が設定されていることが分かります。

定数に設定する数値に意味がある場合、Iota 識別子は使用してはならない

いくつかの実装を試して、Iota 識別子により定数にインクリメントされた値を設定できることが分かりました。
このインクリメントされた値ですが、意味を持つことは危険です。

先ほどの例を振り返ります。

main.go
package main

type TaskStatus int

const (
	notStarted TaskStatus = iota + 1
	inProgress
	done
)

func main() {
	println("notStarted: ", notStarted)
	println("inProgress: ", inProgress)
	println("done: ", done)
}

この実装により設定される整数に以下のように意味を持たせ、整数をアプリケーションロジックで扱うことを想定します。

  • タスクステータス 1 = NOT STARTED
  • タスクステータス 2 = IN PROGRESS
  • タスクステータス 3 = DONE

ここでタスクステータスに新しい概念「PENDING」を追加したくなった際はどうでしょう。

新しい概念「PENDING」を扱うために以下の実装に変更しました。

main.go
package main

type TaskStatus int

const (
	notStarted TaskStatus = iota + 1
	inProgress
	pending
	done
)

func main() {
	println("notStarted: ", notStarted)
	println("inProgress: ", inProgress)
	println("pending: ", pending)
	println("done: ", done)
}

実行してみます。

go run ./main.go
notStarted:  1
inProgress:  2
pending:  3
done:  4

定数 done の値が 3 から 4 に変わることでタスクステータス3は DONE ではなくなりました。
タスクステータス 3 = DONE としてアプリケーションロジックで扱っていた場合、アプリケーションロジックでこの変更に対応しない限りバグを生む可能性があります。

Iota 識別子は列挙的な実装が可能になる便利な言語仕様ですが、こういったリスクを意識して適切に使っていきたいですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?