Goの構造体に関してのアウトプット

## 初めに
Goの構造体を勉強したため、アウトプットのため記事を投稿する。

■ 実行環境
[The Go Playground](https://go.dev/play/)
■ GOのバージョン
1.18

## 概要
構造体とは・・・異なるデータ型を1つにしたもの
1つ1つのデータをフィールド呼ぶ。

## 構造体の宣言

##### 基本形

```go
type [構造体名] struct {
	[フィールド名] [型]
}
```

##### 例

```go
type Go struct {
	X int
	Y int
	S string
}
```

フィールドの型が同じ場合は、カンマを使用して同じ列に記載可能である。

```go
type Go struct {
	X, Y int
	S, T string
}
```

※構造体名の頭文字は小文字でも宣言できるが、その場合は外部のパッケージから参照することができないので、使用用途に合わせて変更する必要がある。
余談だが、Go言語の予約後に「go」という単語があるので、上記例にて頭文字を小文字で使用するとエラーになる。


## 構造体の初期化
構造体の初期化は、値を指定しない方法と値をあらかじめ指定する方法がある。

### 値を指定しない方法
値を指定せずに初期する場合は、3つの初期化方法がある。
いずれの場合も同じように初期化されている。
また、初期化したフィールドの変数にはゼロ値が格納されている。

```go
type Go struct {
	X int
	Y int
	S string
}

func main() { 
    // 1つ目
    one := Go{}
    // 2つ目
	var two Go = Go{}
    // 3つ目
	var three Go

	fmt.Println(one)
	fmt.Println(two)
	fmt.Println(three)
}

// 出力結果
{0 0 }
{0 0 }
{0 0 }
```


### 値を指定する方法
値を指定する場合は、全てのフィールドの変数に値を代入する方法と、一部の変数に値を代入する方法がある。

#### 全てのフィールドの変数に値を代入して初期化

```go
type Go struct {
	X int
	Y int
	S string
}

func main() {
    all := Go{1, 2, "s"}
	fmt.Println(all)
}

// 出力結果
{1 2 s}
```

全ての値をセットしていないとエラーになる。

```go
type Go struct {
	X int
	Y int
	S string
}

func main() {
    // 変数Sの値をセットしていない
    all := Go{1, 2}
	fmt.Println(all)
}

// 出力結果
too few values in struct literal
```
#### 一部のフィールドの変数に値を代入して初期化
一部のフィールドの変数に値を代入して初期化したい場合は、フィールドの変数名を指定して初期化する。
その場合、フィールドを指定していない変数の値はゼロ値となる。

```go
type Go struct {
	X int
	Y int
	S string
}

func main() {
	part := Go{Y: 3}
	fmt.Println(part)
}

// 出力結果
{0 3 }
```

### ポインタを使用して初期化
初期化する際にポインタを使用することができる。

#### &演算子
&演算子を初期化時に使用することができる。
下記コードだと、GOという構造体へのポインタであることが分かる。

##### 例

```go
type Go struct {
	X int
	Y int
}

func main() {
	p := &Go{}
	fmt.Printf("%T\n", p)
}

// 出力結果
*main.Go
```

#### new
newを使用しても、上記の`&演算子`を使用した時と同じように初期化することができる。

```go
type Go struct {
	X int
	Y int
}

func main() {
	p := new(Go)
	fmt.Printf("%T\n", p)
}
// 出力結果
*main.Go
```

### 構造体を初期化しないで使用を試みる
初期化を行わないで構造体を使用しようとするとエラーになる。
そのため、構造体は必ず初期化をして使用する。

```go
func main() {

	type Go struct {
		X int
		Y int
		S string
	}
	fmt.Println(Go)
}

// 出力結果
Go (type) is not an expression
```

## 構造体へのアクセス
構造体へアクセスするには`.`を使用する。

```go
type Go struct {
	X int
	Y int
	S string
}

func main() {
	st := Go{1, 2, "initial"}
	fmt.Println(st)
	st.X = 3
	st.S = "second"
	fmt.Println(st)
}

// 出力結果
{1 2 initial}
{3 2 second}
```

`.`を使用して代入したフィールドXとSの値のみ変更されていることが分かる。

## 値渡しと参照渡し

### 値渡し

```go
type Go struct {
	X int
	Y int
	S string
}

func passValue(pVal Go) {
	// pValを使用して構造体の値を変更
	pVal.X = 2
	pVal.S = "値渡し"
	fmt.Println("仮引数 =", pVal)
}

func main() {
	// 構造体Goの初期化
	p := Go{}
	// 構造体Goを代入した変数pの型を出力
	fmt.Printf("実引数の型 = %T", p)
	// 変数pを出力
	fmt.Println("\n実引数の型 =", p)
	// 変数pを関数に値渡し
	passValue(p)
	// 値渡し後の変数pを出力
	fmt.Println("値渡し後の変数p =", p)
}

// 出力結果
実引数の型 = main.Go
実引数の型 = {0 0 }
仮引数 = {2 0 値渡し}
値渡し後の変数p = {0 0 }
```

値渡しのため、値渡し後の変数pの値が変化していないことが分かる。

### 参照渡し
```go
type Go struct {
	X int
	Y int
	S string
}

// 構造体Goへのポインタである変数pを仮引数pRefとして受け取る
func passRef(pRef *Go) {
	fmt.Printf("仮引数のポインタ = %p", pRef)
	// pRefを使用して構造体の値を変更
	pRef.X = 2
	pRef.S = "参照渡し"
}

func main() {
	// 構造体Goの初期化
	p := &Go{}
	// 構造体Goへのポインタである変数pのメモリアドレスを出力
	fmt.Printf("実引数のポインタのメモリアドレス = %p", p)
	// ポインタpの型を出力
	fmt.Printf("\n実引数の型 = %T ", p)
	// ポインタpを出力
	fmt.Println("\n実引数 =", p)
	// 構造体Goへのポインタを関数に参照渡し
	passRef(p)
	// 参照渡し後のポインタpを出力
	fmt.Println("\n参照渡し後の実引数 =", p)
}

// 出力結果
実引数のポインタのメモリアドレス = 0xc00005c020
実引数の型 = *main.Go 
実引数 = &{0 0 }
仮引数のポインタ = 0xc00005c020
参照渡し後の実引数 = &{2 0 参照渡し}
```

実引数と仮引数のメモリアドレスが同様であり、構造体を参照渡しできることが分かる。


## ネスト
構造体はネストして使用できる。
GOはclassが存在しないが、構造体をネストして使用することで、クラスの継承のような機能を享受できる。
今回は、ネストする構造体を「親の構造体」、ネストされる構造体を「子の構造体」という名称で表記する。

### 通常のネスト
ネストする場合は、親の構造体のフィールドに子の構造体の情報を記載する。

```go
func main() {
	type child struct {
		s string
	}
	type parent struct {
		s     string
		chind child
	}

	// 親の構造体であるparentと子の構造体であるchildを一緒に初期化する
	st := parent{"parent structのfield  ", child{"child structのfield"}}
	// 初期化した親の構造体を出力
	fmt.Println(st)
	// 子の構造体であるchildのフィールドの値を、親の構造体であるparent経由でアクセスして変更する
	st.chind.s = "child structの値を変更します"
	fmt.Println(st)
}

// 出力結果
{parent structのfield   {child structのfield}}
{parent structのfield   {child structの値を変更します}}
```

子の構造体である、childのsというフィールドの値が変更されていることが分かる。

子の構造体を初期化して変数に代入し、その変数を親の構造体の初期化時に使用することもできる。

```go
func main() {
	type child struct {
		s string
	}
	type parent struct {
		s     string
		chind child
	}
	// 子の構造体であるchildを初期化する
	c := child{"childを初期化します"}
	st := parent{"parent structのfield  ", c}
	fmt.Println(st)
}

// 出力結果
{parent structのfield   {childを初期化します}}
```

## スライス
スライスの要素に構造体を格納することもできる。

##### 例
```go
func main() {
	var Go = []struct {
		X int
		S string
	}{
		{
			X: 0,
			S: "要素0",
		},
		{
			X: 1,
			S: "要素1",
		},
	}
	fmt.Println(Go)
	Go[0].S = "change"
	fmt.Println(Go)
}

// 出力結果
[{0 要素0} {1 要素1}]
[{0 change} {1 要素1}]
```

構造体のフィールドにスライスを格納することもできる。

##### 例
```go
func main() {
	type Go struct {
		X []int
	}
	// 初期化
	st := Go{}
	fmt.Println(st)
	// フィールドXにスライスを代入
	st.X = []int{1, 2, 3}
	fmt.Println(st)
}

// 出力結果
{[]}
{[1 2 3]}
```

## 構造体のフィールドに構造体を使用する

構造体のフィールドに構造体を使用することもできる。

```go
type A struct {
	S string
}

type B struct {
	S string
	A A
}

func main() {
	var b B
	fmt.Printf("(%v, %T)\n", b.A, b.A) // ({}, main.A)
	b.A = A{"S"}
	fmt.Printf("(%v, %T)\n", b.A, b.A)     // ({S}, main.A)
	fmt.Printf("(%v, %T)\n", b.A.S, b.A.S) // (S, string)
}
```