在for-range中使用值到引用的棘手情况

huangapple go评论91阅读模式
英文:

Tricky case with value-to-references in for-range

问题

看一下这段代码,你认为输出会是什么?它返回的是"Third"而不是"Second",我花了一些时间才理解为什么。

你知道原因吗?

我对按值传递和按引用传递的概念非常了解,但对于像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 findRecord(id int) (foundRecord *Record) {
    for _, record := range records {
        if record.Id == id {
            foundRecord = &record
            // 你可能认为我们可以在这里使用break,但想象一下我们需要做...
        }
        // ...更多的操作
    }
    return foundRecord
}

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

在线运行代码查看结果:https://play.golang.org/p/Y_iAl6m7Ms

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

英文:

Look to the code - what do you think the output would be? It's return "Third" instead of "Second" and took me a while to understand why.

Do you know a reason?

I get concept of pass-by-value & pass-by-reference quite well but this case is a bit tricky for people coming from languages like Python. So I decided it worth to share.

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)
	}
}

Run it online to check: https://play.golang.org/p/Y_iAl6m7Ms

I've spent some time figuring out what's going on.

答案1

得分: 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 findRecord(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 := findRecord(2)
    if foundRecord == nil {
        fmt.Println("Nothing found")
    } else {
        fmt.Println("Found:", foundRecord.Name)
    }
}

Playground

英文:

You are returning pointer to a record variable which is reused by each iteration of the loop. Last iteration sets the pointer to the third structure.

The reuse of variable has an enormous advantage. It does not allocate memory in every iteration of the loop. This saves a lot of garbage collection time.

This is well know behaviour described in FAQ.

To fix it, return a pointer to an element of the slice. It is safe as slice elements are referencable in 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)
	}
}

<kbd>Playground</kbd>

huangapple
  • 本文由 发表于 2016年4月1日 18:01:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/36353406.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定