これらのタイプがあるとしましょう:
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
から直接ポインターを取得することは可能ですか?
明らかに、反復のためだけに構造を変更したくはありません。より冗長な解決策は解決策ではありません。
いいえ、必要な略語は使用できません。
この理由は、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]
を配列/スライスの2番目の値として使用します。これは、値がコピーされ、元の値が変更できないことを効果的に意味します。
この動作は 次のコード で示されています:
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{
{"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
は比較的小さく、2つの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"
}
}
私はあなたの最後の提案を採用し、範囲のインデックスのみのバージョンを使用します。
for i := range n.Attr {
if n.Attr[i].Key == "href" {
n.Attr[i].Val = "something"
}
}
Key
をテストする行とVal
を設定する行の両方でn.Attr[i]
を明示的に参照する方が、一方にattr
を使用し、もう一方にn.Attr[i]
を使用するよりも簡単に思えます。
例えば:
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:""}