GoLang:在范围内具有值到引用的棘手情况

时间:2016-04-01 10:01:37

标签: go pass-by-reference pass-by-value

查看代码 - 您认为输出是什么?它返回“第三”而不是“第二”,并花了一些时间来理解为什么。

你知道原因吗?

我得到了传值和概念的概念。传递参考相当不错,但这种情况对于来自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

我花了一些时间搞清楚发生了什么。

1 个答案:

答案 0 :(得分:5)

您正在返回指向record变量的指针,该变量由循环的每次迭代重用。最后一次迭代将指针设置为第三个结构。

变量的重用具有巨大的优势。它不会在循环的每次迭代中分配内存。这节省了大量的垃圾收集时间。

这是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