struct【Go】
宣言
変数をまとめたもの、すなわち構造体を意味します。
type Vertex struct{ X, Y int } func main(){ v := Vertex{X:1, Y:2} // v := Vertex{1, 2} // このようにも書ける v.X = 100 fmt.Println(v) // {100 2} fmt.Println(v.X, v.Y) // 100 2 }
structのフィールド名は頭文字を大文字にしないと、外部パッケージからアクセスすることができません。
mapやスライスとは異なり、空の宣言をしてもnilにはならず、初期値(intは0、stringは"")が設定されます。
var v2 = Vertex{} fmt.Println(v2) // {0 0} v3 := Vertex{} fmt.Println(v3) // {0 0}
また、ポインタを宣言する場合は次のようにします。
v4 := new(Vertex) fmt.Println(*v4) // {0 0} // 番地の実態を参照 v5 := &Vertex{} fmt.Println(*v5) // {0 0} // 番地の実態を参照
1行見ただけで、ポインタを参照することがわかるため、
&Vertex{}による宣言が使われることが多いようです。
関数による変更
mapやスライスと同様、関数を用いて要素を変更するには、値ではなくポインタを渡す必要があります。
値渡しの場合の例です。値渡しの場合、コピーが作成され、コピーに対して外部関数内部の処理が行われます。
引数として渡したstructと外部関数の中で処理されるstructのアドレスが、異なっているのがわかります。
type Vertex struct{ X, Y int } func changeVertexItem(v Vertex) { // 値渡し v.X = 100 // 値渡しなので、コピーされた実体に対して処理される。 fmt.Println(&v.X, v) // 0xc000078120 {100 2} アドレスが異なる } func main(){ v := Vertex{1, 2} // 値による宣言 changeVertexItem(v) fmt.Println(&v.X, v) // 0xc000078060 {1 2} アドレスが異なる }
ポインタ渡しにすると引数として渡したstructの値が変わります。
外部関数と同じ物を参照しているからです。
type Vertex struct{ X, Y int } func changeVertexItem2(v *Vertex) { // ポインタ渡し v.X = 100 fmt.Println(&v.X, *v) // 0xc0000780f0 {100 2} アドレスは同じ } func main(){ v2 := &Vertex{1, 2} // ポインタによる宣言 changeVertexItem2(v2) fmt.Println(&v2.X, *v2) // 0xc0000780f0 {100 2} アドレスは同じ }
ちなみに、ポインタ渡しの場合、structの中の要素(上記例でいうとv.Xまたはv.Y)のアドレスは同じですが、
structそのもの(上記例でいうとv)のアドレスは、外部関数とmain関数とで異なります。
それでも、中の要素が同じ箇所を見ているので、問題なく処理できるのです。