いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-90 (Go の enum と iota の使い方メモ)

概要

Go には、C#などのように enum という型がありませんが、定数を使って同じような感じにすることが出来ます。

enum と iota に関して、とても参考になった記事があったので、忘れない内にメモメモ。

★ Ultimate Visual Guide to Go Enums and iota | by Inanc Gumus | Learn Go Programming

ついでに自分でもサンプル作ったので、以下にメモメモ。

Enum のサンプル

package enum

import (
    "github.com/devlights/gomy/output"
)

type (
    // Status -- サンプル用 enum. 何かのステータスを表す
    Status int
)

// impl -- fmt.Stringer
func (s Status) String() string {
    values := [...]string{
        "不明",
        "実行中",
        "停止中",
    }

    if s < Unknown || Stopped < s {
        return "不明"
    }

    return values[s]
}

// CanForward -- 先に進めることができるかどうかを返す
//
// 可能な場合は true, それ以外は false
func (s Status) CanForward() bool {
    switch s {
    case Running:
        return true
    }

    return false
}

// ステータス値
const (
    Unknown Status = 0 // 不明
    Running Status = 1 // 実行中
    Stopped Status = 2 // 停止中
)

// GoEnums -- Go における enum の扱い方についてのサンプルです
//
// REFERNCES:
//   -https://blog.learngoprogramming.com/golang-const-type-enums-iota-bc4befd096d3
func GoEnums() error {
    // ---------------------------------------------------------------------------
    // enum とは関連する値を一つの型でグルーピングするもの
    //
    // 例: シャツのサイズ (S, M, L), ステータス (Running, Stopped, Resumed), 曜日
    // ---------------------------------------------------------------------------
    v := Running
    output.Stdoutl("[Status]", int(v))

    // ---------------------------------------------------------------------------
    // 型を定義するので、振る舞いを追加することができる
    //
    // 有名なのが golang.org/x/tools/cmd/stringer で、指定した enum の名称を出力する
    // String() を自動生成してくれるもの
    //
    // $ go get -u -v golang.org/x/tools/cmd/stringer
    // $ stringer -type Status
    //
    // 上記で status_string.go が出力される
    // ---------------------------------------------------------------------------
    output.Stdoutl("[Status]", v)
    output.Stdoutl("[v.CanForward]", v.CanForward())
    output.Stdoutl("[Running.CanForward]", Running.CanForward())
    output.Stdoutl("[Stopped.CanForward]", Stopped.CanForward())

    return nil
}

try-golang/go_enums.go at master · devlights/try-golang · GitHub

実行すると以下のようになります。

$ make run
ENTER EXAMPLE NAME: go_enums
[Name] "go_enums"
[Status]             1
[Status]             実行中
[v.CanForward]       true
[Running.CanForward] true
[Stopped.CanForward] false


[Elapsed] 0s

iota のサンプル

package enum

import (
    "github.com/devlights/gomy/output"
)

// GoIota -- Go における iota の扱い方についてのサンプルです
//
// REFERENCES:
//   -https://blog.learngoprogramming.com/golang-const-type-enums-iota-bc4befd096d3
//
//noinspection GoBoolExpressions
func GoIota() error {
    // ---------------------------------------------------------------------------
    // iota は Goで利用できる数値のカウンタ.
    // 以下の基本的な特徴を持つ.
    //
    // - 0から始まる
    // - 呼び出すたびに1ずつ加算していく
    // - const定義の中でのみ利用できる
    // - リセットできる
    //
    // ---------------------------------------------------------------------------
    type (
        Weekday int
    )

    // 基本的な使い方
    const (
        _      Weekday = iota // 初期値は 0. 曜日は 1 からスタートしたいので捨てる
        Sunday                // = iota を省略可能。値は1となる.
        _                     // iota の値は加算されるが捨てる。つまり2を捨てる
        // コメントの場合 iota は加算をスキップ。なので増えない。空行の場合も増えない

        Monday  // 3
        Tuesday // 4
    )

    output.Stdoutl("Weekday", Sunday, Monday, Tuesday)

    // iota は const ブロック 毎にリセットされる
    const (
        Sunday2 Weekday = iota // 0
        Monday2         = iota // 1
    )

    output.Stdoutl("Weekday2", Sunday2, Monday2)

    // iota を 途中で使うと、そこまで毎行 iota を使っていた状態の値を返す
    // つまり、以下の場合だと Three の部分で iota を書くと 2 となる
    // (One の部分で 0 -> 1, Two の部分で 1 -> 2 だから)
    const (
        One   = 1        // iota 0 -> 1
        Two   = 2        // iota 1 -> 2
        Three = iota + 1 // iota 2 -> 3
        Four  = iota + 1 // iota 3 -> 4
    )

    output.Stdoutl("iota in the middle", One, Two, Three, Four)

    // iota を同じ行に複数書いても同じ値となる
    const (
        A, B, C = iota, iota, iota // 0, 0, 0
        D, E, F                    // 1, 1, 1
    )

    output.Stdoutl("iota multiple", A, B, C, D, E, F)

    // iota を同じ行に複数書くと同じ iota の値を受け取れる
    const (
        V1, V2 = iota, iota + 10 // 0, 10
        V3, V4                   // 1, 11
        // この場合、以下のように一行で一つだけの宣言は *そのまま* できない. コンパイルエラーとなる
        // 上で一行に2つの iota のパターンを利用しているため
        // V5,

        // iota をリセットするのであればオッケイ
        V5 = iota
    )

    output.Stdoutl("iota multiple2", V1, V2, V3, V4, V5)

    // Go の const では 直近の表現がキープされる
    // なので、以下のように 直値 で 1 と指定して次の行に値を指定しない場合
    // 直近の表現、つまり 1 がそのままキープされる。
    //
    // なので、iota を指定している場合は、暗黙で次の行でも iota が指定されていることになる
    const (
        First  = 1
        Second // Second = 1 と同じ
    )

    output.Stdoutl("last used expression keep", First, Second)

    // iota で フラグ向けの値はよく利用されるパターン
    // ビット操作で利用することが多い
    type (
        Flg int
    )
    const (
        Flg1    Flg = 1 << iota // 1 << 0 --> 0b00000001 --> 1
        Flg2                    // 1 << 1 --> 0b00000010 --> 2
        Flg3                    // 1 << 2 --> 0b00000100 --> 4
        AllFlgs = Flg1 | Flg2 | Flg3
    )

    flg := Flg2 | Flg3
    output.Stdoutl("iota bitwise value is", flg)

    if flg&Flg1 == Flg1 {
        output.Stdoutl("flg & Flg1", "Flg1")
    }

    if flg&Flg2 == Flg2 {
        output.Stdoutl("flg & Flg2", "Flg2")
    }

    if flg&Flg3 == Flg3 {
        output.Stdoutl("flg & Flg3", "Flg3")
    }

    if flg&AllFlgs != 0 {
        output.Stdoutl("flg & AllFlgs", "flg is valid")
    }

    wrongFlg := Flg(8) // 8 --> 1 << 3 --> 0b00001000
    if wrongFlg&AllFlgs == 0 {
        output.Stdoutl("Flg(8) & AllFlgs", "Flg(8) is invalid")
    }

    return nil
}

try-golang/go_iota.go at master · devlights/try-golang · GitHub

実行すると以下のようになります。

$ make run
ENTER EXAMPLE NAME: go_iota
[Name] "go_iota"
Weekday              1 3 4
Weekday2             0 1
iota in the middle   1 2 3 4
iota multiple        0 0 0 1 1 1
iota multiple2       0 10 1 11 2
last used expression keep 1 1
iota bitwise value is 6
flg & Flg2           Flg2
flg & Flg3           Flg3
flg & AllFlgs        flg is valid
Flg(8) & AllFlgs     Flg(8) is invalid


[Elapsed] 998.2µs

参考

golang.org

golang.org

github.com

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com