我们假设我有这些类型:
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
获取指针?
显然,我不想仅为迭代更改结构,而更详细的解决方案也不是解决方案。
答案 0 :(得分:126)
不,你想要的缩写是不可能的。
原因是range
复制了您正在迭代的切片中的值。
specification about 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
因此,range使用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已经提出的那样。
答案 1 :(得分:29)
你似乎在寻求与此相当的东西:
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}]}
这样可以避免创建一个 - 可能很大 - 类型为Attribute
的副本,但代价是切片边界检查。在您的示例中,类型Attribute
相对较小,两个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
子句获取等效结果的方法是:
for i, attr := range n.Attr {
if attr.Key == "href" {
n.Attr[i].Val = "something"
}
}
答案 2 :(得分:14)
例如:
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:""}
答案 3 :(得分:14)
我会调整你的最后一个建议并使用范围的仅索引版本。
for i := range n.Attr {
if n.Attr[i].Key == "href" {
n.Attr[i].Val = "something"
}
}
在测试n.Attr[i]
的行和设置Key
的行中明确地引用Val
而不是使用attr
来表示n.Attr[i]
似乎更简单另一个{{1}}。