ProgrammingGo バックエンドエンジニア

Go における map と slice の操作における range メカニズムについて説明してください。ループ変数の使用における細かい点と直面する可能性のあるエラーは何ですか?

Hintsage AIアシスタントで面接を突破

回答

for ... range ループは、スライス (slice)、マップ (map)、配列、または文字列の要素を便利に繰り返し処理することができます。

  • スライスの場合:各イテレーションでインデックスと要素のコピーを返します。
  • マップの場合:キーと値のコピーを返します。

例:

s := []int{1, 2, 3} for i, v := range s { fmt.Println(i, v) } m := map[string]int{"a":1, "b":2} for k, v := range m { fmt.Println(k, v) }

重要な注意点: 変数 ivk などはすべてのイテレーションで再利用されます!これは、ループ内で参照渡しを行ったり、range 内で goroutine を起動したりするときにバグの原因となることがよくあります。

誤解を招く質問

スライス内で goroutine を起動し、イテレーション変数をキャプチャした場合、どうなりますか?どのようにエラーを回避することができますか?

回答: 典型的なエラーが発生します:goroutine 内で使用されるループ変数は、ループの終了後に最後の値を持ちます。回避するには、ローカルコピーを作成する必要があります:

nums := []int{1, 2, 3} for _, v := range nums { go func(val int) { fmt.Println(val) }(v) }

知識不足による実際のエラーの例


事例

あるプロジェクトでは、複数の goroutine を介してスライスを填補するために range を使用しましたが、ループ変数のコピーを作成するのを忘れてしまいました。すべての goroutine が配列の最後の値を印刷し、ビジネスロジックが大きく損なわれました。


事例

マップの range では、値への参照を新しいポインタのスライスに保存しました。その結果、新しいスライスのすべての要素が、ループ内で使用される同じ変数(値のコピー)を参照しました。この変数をループの外で更新した時にバグが発生しました(panic: invalid memory address または予期しないデータ)。


事例

内部ツールで、string の range 中に重要な部分文字列のハンドラを起動しましたが、各イテレーションでバイトのオフセットを取得し、Unicode の文字ではありませんでした。結果として、Unicode 文字列の不正な処理や文字の不正なスライスが発生しました。