在golang中迭代时更改值
假设我有这些types:
type Attribute struct { Key, Val string } type Node struct { Attr []Attribute } 而且我想迭代我的节点的属性来改变它们。
我本来希望能够做到
 for _, attr := range n.Attr { if attr.Key == "href" { attr.Val = "something" } } 
 但attr不是一个指针,这是行不通的,我必须这样做 
 for i, attr := range n.Attr { if attr.Key == "href" { n.Attr[i].Val = "something" } } 
 有更简单还是更快的方法? 是否有可能直接从range指针? 
显然,我不想仅仅为了迭代而改变结构,更详细的解决scheme是无法解决的
不,你想要的缩写是不可能的。
 原因是range复制了您正在迭代的切片的值。  关于范围的规范说: 
Range expression 1st value 2nd value (if 2nd variable is present) array or slice a [n]E, *[n]E, or []E index i int a[i] E
 因此,范围使用a[i]作为数组/切片的第二个值,这实际上意味着该值被复制,使原始值不可触摸。 
以下代码演示了此行为:
 x := make([]int, 3) x[0], x[1], x[2] = 1, 2, 3 for i, val := range x { println(&x[i], "vs.", &val) } 
代码打印完全不同的内存位置,范围内的值和切片中的实际值:
 0xf84000f010 vs. 0x7f095ed0bf68 0xf84000f014 vs. 0x7f095ed0bf68 0xf84000f018 vs. 0x7f095ed0bf68 
所以你唯一能做的就是使用指针或者索引,就像jnml和peterSO已经提出的那样。
例如:
 package main import "fmt" type Attribute struct { Key, Val string } type Node struct { Attr []*Attribute } func main() { n := Node{[]*Attribute{ &Attribute{"foo", ""}, &Attribute{"href", ""}, &Attribute{"bar", ""}, }} for _, attr := range n.Attr { if attr.Key == "href" { attr.Val = "something" } } for _, v := range n.Attr { fmt.Printf("%#v\n", *v) } } 
操场
产量
 main.Attribute{Key:"foo", Val:""} main.Attribute{Key:"href", Val:"something"} main.Attribute{Key:"bar", Val:""} 
替代方法:
 package main import "fmt" type Attribute struct { Key, Val string } type Node struct { Attr []Attribute } func main() { n := Node{[]Attribute{ {"foo", ""}, {"href", ""}, {"bar", ""}, }} for i := range n.Attr { attr := &n.Attr[i] if attr.Key == "href" { attr.Val = "something" } } for _, v := range n.Attr { fmt.Printf("%#v\n", v) } } 
操场
输出:
 main.Attribute{Key:"foo", Val:""} main.Attribute{Key:"href", Val:"something"} main.Attribute{Key:"bar", Val:""} 
你似乎在要求这样的东西:
 package main import "fmt" type Attribute struct { Key, Val string } type Node struct { Attr []Attribute } func main() { n := Node{ []Attribute{ {"key", "value"}, {"href", "http://www.google.com"}, }, } fmt.Println(n) for i := 0; i < len(n.Attr); i++ { attr := &n.Attr[i] if attr.Key == "href" { attr.Val = "something" } } fmt.Println(n) } 
输出:
 {[{key value} {href http://www.google.com}]} {[{key value} {href something}]} 
 这样可以避免创buildtypesAttribute值的可能较大的副本,但要牺牲片边界检查。 在你的例子中,typesAttribute是比较小的,两个string切片引用:64位体系结构机器上的2 * 3 * 8 = 48字节。 
你也可以简单地写:
 for i := 0; i < len(n.Attr); i++ { if n.Attr[i].Key == "href" { n.Attr[i].Val = "something" } } 
 但是,用range子句获得等效结果的方法是创build一个副本,但最小化分片边界检查: 
 for i, attr := range n.Attr { if attr.Key == "href" { n.Attr[i].Val = "something" } } 
我会适应你的最后一个build议,并使用索引版本的范围。
 for i := range n.Attr { if n.Attr[i].Key == "href" { n.Attr[i].Val = "something" } } 
 对于我来说,在testingKey的行和设置Val的行中都明确提到n.Attr[i]似乎比较简单,而不是使用attr作为一个,而使用n.Attr[i]作为另一个。