2016-04-01 6 views
2

コードを見てください - 出力はどうなると思いますか?それは "Second"の代わりに "Third"を返し、なぜか理解するために私をしばらく捉えました。GoLang:for-rangeでの値の参照が厄介なケース

あなたは理由を知っていますか?

私は値渡し&の概念をかなりよく受け取りましたが、このケースはPythonのような言語から来た人にとってはやや難解です。だから私はそれを共有する価値があると決めました。それはオンラインチェックする

package main 

import "fmt" 

type Record struct { 
    Id int 
    Name string 
} 

var records = []Record{ 
    Record{1, "First"}, 
    Record{2, "Second"}, 
    Record{3, "Third"}, 
} 

func findRecod(id int) (foundRecord *Record) { 
    for _, record := range records { 
     if record.Id == id { 
      foundRecord = &record 
      // You think we can do a break here but imagine we need to do... 
     }  
     // ...something more here 
    } 
    return foundRecord 
} 

func main() { 
    foundRecord := findRecod(2) 
    if foundRecord == nil { 
     fmt.Println("Nothing found") 
    } else { 
     fmt.Println("Found: ", foundRecord.Name) 
    } 
} 

ランは:https://play.golang.org/p/Y_iAl6m7Ms

私は何が起こっているのを考え出すいくつかの時間を費やしてきました。

+0

私はdownvoteなかった –

+0

。あなたはダウン投票した場合、私はコメントで理由をお知らせください。:(ダウンは、投票した。しかし、そのせいか「期待していたものを返すのではなく、その理由を説明できますか」ということで、あなたが期待したことを説明していないので、バグタイプの質問が見つかるようです。 –

+1

@AH 、おかげで、 "see-my-code-find-the-bug"ではないことをより明確にするために投稿を編集しました。 –

答えて

4

ループの各繰り返しで再利用される変数recordへのポインタを返します。最後の反復でポインタを3番目の構造体に設定します。

変数の再利用には大きな利点があります。ループの繰り返しごとにメモリを割り当てません。これにより、ガベージコレクションの時間を大幅に節約できます。

これはよく知られている動作で、FAQです。

これを修正するには、スライスの要素へのポインタを戻します。スライス要素はGoで参照可能であるため安全です。

package main 

import "fmt" 

type Record struct { 
    Id int 
    Name string 
} 

var records = []Record{ 
    Record{1, "First"}, 
    Record{2, "Second"}, 
    Record{3, "Third"}, 
} 

func findRecod(id int) (foundRecord *Record) { 
    for i, record := range records { 
     if record.Id == id { 
      foundRecord = &records[i] 
      // You think we can do a break here but imagine we need to do... 
     }  
     // ...something more here 
    } 
    return foundRecord 
} 

func main() { 
    foundRecord := findRecod(2) 
    if foundRecord == nil { 
     fmt.Println("Nothing found") 
    } else { 
     fmt.Println("Found: ", foundRecord.Name) 
    } 
} 
疑問だった私はなぜだろ

Playground

+0

はい、私は約10分のログ&挑戦だったo把握する。あなたが注意を払っていない場合、間違いなく簡単に見逃すことができます。そして、Pythonを使用した後、少し怠け者になってしまいました。それは分かち合うのがよいと思った。 –

+1

より効率的なバージョンは、[this](http://play.golang.org/p/SAul2jRSH7)のようなもので、forの範囲に余分なコピーはありません。 – OneOfOne

関連する問題