Computer368520 views
中学英語812106 views
高校日本史190647 views
小学社会310694 views
いろは3014035 views
ヒストリア291495 views
高校物理160596 views
雑学1473736 views
高校生物552029 views
中学社会669043 views
Help
Tools

English

Go のマップの存在チェック(コンマOK イディオム)

マップから値を取得するとき、そのキーが存在するかどうかを確認したい場面がよくある。Go では「コンマOK イディオム」を使ってこれを実現する。

存在しないキーの問題

マップに存在しないキーでアクセスすると、値の型のゼロ値が返る。

ages := map[string]int{
    "Alice": 30,
}

fmt.Println(ages["Bob"]) // 0

これだと「Bob の年齢が 0」なのか「Bob が存在しない」のか区別できない。

コンマOK イディオム

マップの値を取得するとき、2つ目の戻り値でキーの存在を確認できる。

ages := map[string]int{
    "Alice": 30,
    "Bob":   0,
}

age, ok := ages["Bob"]
fmt.Println(age, ok) // 0 true

age, ok = ages["Charlie"]
fmt.Println(age, ok) // 0 false

ok が true ならキーが存在し、false なら存在しない。これで「値が 0」と「キーがない」を区別できる。

条件分岐での使い方

よくあるパターンとして、if 文と組み合わせて使う。

if age, ok := ages["Alice"]; ok {
    fmt.Printf("Alice is %d years old\n", age)
} else {
    fmt.Println("Alice not found")
}

この書き方だと age 変数のスコープが if ブロック内に限定されるため、コードがすっきりする。

存在確認だけしたい場合

値が不要で存在確認だけしたいときは、ブランク識別子を使う。

if _, ok := ages["Alice"]; ok {
    fmt.Println("Alice exists")
}

実践的な例

ユーザーのキャッシュを管理する例を見てみよう。

type User struct {
    ID   int
    Name string
}

var userCache = make(map[int]*User)

func GetUser(id int) (*User, error) {
    // キャッシュを確認
    if user, ok := userCache[id]; ok {
        return user, nil
    }
    
    // キャッシュになければDBから取得
    user, err := fetchUserFromDB(id)
    if err != nil {
        return nil, err
    }
    
    // キャッシュに保存
    userCache[id] = user
    return user, nil
}

値がゼロ値かどうかの判定

コンマOK イディオムを使わないと、意図しない動作になることがある。

コンマOK なし

値がゼロ値のとき、キーが存在するのかどうか判断できない。

コンマOK あり

キーの存在を明確に判定できる。ゼロ値が有効な値として使われる場合に必須。

counts := map[string]int{
    "apple":  5,
    "banana": 0, // 在庫ゼロだが存在する
}

// 悪い例
if counts["banana"] == 0 {
    fmt.Println("banana not found or out of stock")
}

// 良い例
if count, ok := counts["banana"]; ok {
    if count == 0 {
        fmt.Println("banana is out of stock")
    }
} else {
    fmt.Println("banana not found")
}

コンマOK イディオムは Go のマップを扱う上で必須の知識だ。キーの存在を確認するときは常にこの方法を使うようにしよう。