英文:
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)
}
}
英文:
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论