开门见山,在golang中,for range 中,接收到的item用的是同一个地址,因为for range创建了每个元素的副本,而不是直接返回每个元素的引用,也就是说,以下代码将会不符合预期:

package main

import "fmt"

type A struct {
    AA string `json:"aa"`
}

type B struct {
    AA *string `json:"aa"`
}

func main() {
    arr := []A{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}
    res := make([]*B, 0)
    for i, item := range arr {
        fmt.Printf("item: %p, AA:%p, index:%v\n", &item, &item.AA, i)
        res = append(res, &B{AA: &item.AA})
    }
    for _, b := range res {
        fmt.Printf("%v ", *b.AA)
    }
}

输出:

item: 0xc000116210, AA:0xc000116210, index:0
item: 0xc000116210, AA:0xc000116210, index:1
item: 0xc000116210, AA:0xc000116210, index:2
item: 0xc000116210, AA:0xc000116210, index:3
item: 0xc000116210, AA:0xc000116210, index:4
5 5 5 5 5 

是的没错,最终res的值是555,这里有点反常识

因为for range每次遍历的时候,都拷贝了一份到item里面,并且item的指针永远指向同一个,因此,最终他们的值都是5

如何解决?很简单,直接访问slice中的元素而非访问拷贝的元素即可:

func main() {
    arr := []A{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}
    res := make([]*B, 0)
    //for i, item := range arr {
    //    fmt.Printf("item: %p, AA:%p, index:%v\n", &item, &item.AA, i)
    //    res = append(res, &B{AA: &item.AA})
    //}
    for i, _ := range arr {
        res = append(res, &B{AA: &arr[i].AA})
    }
    for _, b := range res {
        fmt.Printf("%v ", *b.AA)
    }
}

输出:

1 2 3 4 5
最后修改:2022 年 10 月 19 日
如果觉得我的文章对你有用,请随意赞赏