for-range 中的值引用的棘手案例

Posted

技术标签:

【中文标题】for-range 中的值引用的棘手案例【英文标题】:Tricky case with value-to-references in for-range 【发布时间】:2016-07-21 02:06:18 【问题描述】:

查看代码 - 您认为输出会是什么?它返回“第三”而不是“第二”,我花了一段时间才明白为什么。

你知道原因吗?

我很好地理解了按值传递和按引用传递的概念,但这种情况对于来自 Python 等语言的人来说有点棘手。所以我觉得值得分享。

package main

import "fmt"

type Record struct 
    Id int
    Name string


var records = []Record
    Record1, "First",
    Record2, "Second",
    Record3, "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

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

【问题讨论】:

我想知道为什么这个问题被否决了:(。如果你投了反对票,请在评论中告诉我原因。 我没有投反对票。但这可能是因为当您说“它返回的结果不是我所期望的。您能解释原因”时,您没有解释您的期望。所以它看起来像是“查看我的代码找到错误”类型的问题。 @AH,谢谢,我已经编辑了帖子以使其更清楚地表明它不是“see-my-code-find-the-bug”。 【参考方案1】:

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

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

这是FAQ 中描述的众所周知的行为。

要修复它,返回指向切片元素的指针。这是安全的,因为切片元素在 Go 中是可引用的。

package main

import "fmt"

type Record struct 
    Id int
    Name string


var records = []Record
    Record1, "First",
    Record2, "Second",
    Record3, "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

【讨论】:

是的,我在大约 10 分钟的记录和凝视后得到了它。我的案例有更多的代码,所以要弄清楚是一个挑战。如果您不注意,很容易忽略。在使用 Python 之后,我有点懒惰了。觉得分享会很好。 请注意,更高效的版本将类似于 this,而在 for 范围内没有额外的副本。

以上是关于for-range 中的值引用的棘手案例的主要内容,如果未能解决你的问题,请参考以下文章

引用与泛型不明确

go的值类型和引用类型2——内存分配规则

Golang 语言坑之for-range

java中的值传递和引用传递

Swift 中的值类型与引用类型

通过取消引用更改 NSArray 中的值?