For loop 如何修改切片中结构的字段?

For loop 如何修改切片中结构的字段?,for-loop,go,struct,iteration,variable-assignment,For Loop,Go,Struct,Iteration,Variable Assignment,我有一个名为test.JSON的JSON文件,其中包含: [ { “姓名”:“约翰”, “兴趣”:[“曲棍球”、“骑师”] }, { “姓名”:“利马”, “兴趣”:[“吃”,“扑克”] } ] 现在我已经编写了一个golang脚本,它将JSON文件读取到结构的一个片段,然后在条件检查后,通过迭代该片段来修改结构字段 以下是我到目前为止所做的尝试: 主程序包 进口( “日志” “字符串” “io/ioutil” “编码/json” ) 类型子数据库结构{ 名称字符串`json:“名称”` 兴趣

我有一个名为
test.JSON
的JSON文件,其中包含:

[
{
“姓名”:“约翰”,
“兴趣”:[“曲棍球”、“骑师”]
},
{
“姓名”:“利马”,
“兴趣”:[“吃”,“扑克”]
}
]
现在我已经编写了一个golang脚本,它将JSON文件读取到结构的一个片段,然后在条件检查后,通过迭代该片段来修改结构字段

以下是我到目前为止所做的尝试:

主程序包
进口(
“日志”
“字符串”
“io/ioutil”
“编码/json”
)
类型子数据库结构{
名称字符串`json:“名称”`
兴趣[]字符串`json:“兴趣”`
}
var dbUpdate[]subDB
func getJSON(){
//打开文件
文件名:=“test.json”
val,err:=ioutil.ReadFile(文件名)
如果错误!=零{
log.Fatal(错误)
}
err=json.Unmarshal(val和dbUpdate)
}
func(v*subDB)更改(newresponse[]字符串){
v、 兴趣=新的回应
}
func更新程序(名称字符串、新兴趣字符串){
//在结构的切片上迭代
对于u,项:=范围dbUpdate{
//检查提供的名称是否与当前结构匹配
if strings.Contains(item.Name,Name){
flag:=false//声明一个标志变量
//兴趣是一个切片,所以我们对它进行迭代
对于u,intr:=范围项。兴趣{
//检查newinterest是否在切片值的任意一个范围内
if strings.Contains(intr、newinterest){
flag=true
break//如果找到一个,则终止循环
}
}
//如果flag为false,则更改兴趣字段
//当前结构的
如果!旗帜{
//利益掌握着一条线
item.Change([]字符串{newinterest})//传递字符串片段
}
}
}
}
func main(){
getJSON()
更新程序(“利马”,“慢跑”)
log.Printf(“%+v\n”,dbUpdate)
}
我得到的结果是:

[{姓名:约翰兴趣:[曲棍球骑师]}{姓名:利马兴趣:[吃扑克]}]
但是,我应该得到如下输出:

[{姓名:约翰兴趣:[曲棍球骑师]}{姓名:利马兴趣:[慢跑]}]
我不太确定我做错了什么,所以如果有人向我指出同样的错误,我将不胜感激。我的理解是,由于
Change()
传递了一个指针,它应该直接修改字段。

问题 让我们引用一下语言规范:

带有“range”子句的“for”语句遍历所有条目 在通道上接收到的数组、切片、字符串或映射或值。 对于每个条目,它将迭代值分配给相应的迭代 变量,然后执行块

那么在

对于u2;,项:=范围dbUpdate{…}
整个语句形成一个作用域,在该作用域中声明一个名为
item
的变量,并将
dbUpdate
的每个元素的值分配给它,然后从第一个到最后一个-语句执行其迭代

Go、always和everywhere中的所有赋值都会将被赋值表达式的值复制到接收该值的变量中

所以,当你

类型子数据库结构{
名称字符串`json:“名称”`
兴趣[]字符串`json:“兴趣”`
}
var dbUpdate[]subDB
您有一个片,其背景数组包含一组元素,每个元素都有类型
subDB

因此,当。。。range在切片上迭代,在每次迭代中,完成当前切片元素中包含的
subDB
值的字段的浅拷贝:这些字段的值被复制到变量
项中

我们可以将发生的事情改写为:

i:=0的
;i
如您所见,如果在循环体中对
item
进行变异,对它所做的更改不会以任何方式影响当前正在迭代的集合元素

解决方案 广义地说,解决方案是完全了解这样一个事实,即Go在它实现的大多数东西中都非常简单,因此
range
对它来说并不是魔术:迭代变量只是一个变量,对它的赋值只是一个赋值

至于解决特定问题,有多种方法

通过索引引用集合元素 做

i:=范围dbUpdate的
{
dbUpdate[i]。字段名=值
}
由此推论,有时,当元素很复杂或您想将其变异委托给某个函数时,您可以使用指向它的指针:

i:=范围dbUpdate的
{
p:=&dbUpdate[i]
变异subdb(p)
}
...
func mutateSubDB(p*subDB){
p、 SomeField=someValue
}
将指针保留在切片中 如果你的切片像

var dbUpdates[]*subDB
…并保留指向(通常为堆分配的)
SubDB
值的指针,

对于u2;,ptr:=range dbUpdate{…}
语句自然会将指向SubDB(匿名)变量的指针复制到
ptr
中,因为切片包含指针,所以赋值会复制指针

由于包含相同地址的所有指针都指向相同的值,因此通过保留在迭代变量中的指针对目标变量进行变异将使切片元素所指向的对象发生变异

选择哪种方法通常应该取决于考虑因素,而不是考虑如何在元素上迭代——这仅仅是因为一旦您理解了代码不起作用的原因,您就不再有这个问题了

通常:如果你的值很大,考虑把指针保存到它们上面。 如果你