[Go言語] 初心者必見シリーズ: 構造体（Structs）


# 構造体

構造体は、**フィールド（field）の集まり**です。

## 定義方法

構造体は下記のように `type` と `struct` を使用して定義します。

* 構文

  ```go
  type 構造体の名前 struct {
      フィールド名 型名
      フィールド名 型名
      フィールド名 型名
      ...
  }
  ```

  **同じ型**のフィールド名は、**カンマ区切り**で書くことができます。

  ```go
  type 構造体の名前 struct {
      フィールド名, フィールド名, フィールド名 型名
      フィールド名 型名
  }
  ```

* 例

  人のデータをまとめた構造体です。

  ```go
    type People struct {
            Name string
            Sex string
            Age int
            Height int
            Weight int
    }
  ```

## 初期化

### 方法1：Key:Valueで初期化します。

```go
people := People{Name: "太郎"}
```

設定しない要素は **ゼロ値** で初期化される。

### 方法2：要素の定義順に初期化します。

```go
people := People{"太郎", "男", ...}
```

この方法では、**全部のfieldを設定**する必要があります。

### 方法3：定義後にドット（.）を用いてfieldを初期化します。

```go
people := People{}
people.Sex = "男"
people3.Age = 8
```


## 構造体へのポインター（Pointer to structs）

### 方法1：`&` (アドレス演算子）で構造体へのPointerを取得します。

```go
people := People{Name: "太郎"}
p := &people
(*p).Name = "花子"
```

`(*p)` は `p` に省略できます。 

```go
p.Name = "花子"
```

### 方法2： `new(構造体名)` で構造体へのポインターを生成します。

```go
people := new(People) // Peopleのポインタ型
(*people).Name = "花子"
people.Name = "花子"  // (*people) は peopleに省略できます。
```

> `new(T)` はT型の**無名変数**（unnamed variable）を作成し、それを `T` のゼロ値へ初期化し、 `*T` 型の値であるそのアドレスを返します。

式 `new(People)` と `&People{}` は等価です。


## タグ（Tag）

Go構造体タグは、Go構造体宣言の**フィールドの後に表示される注釈**です。

実行時に`reflect`パッケージでその情報を取得し利用したりします。

良くある構造体をJSONに変換する例として、

下記のようにフィールドの後ろに `json` タグをつけると、

```go
  type People struct {
          Name string		`json:"name"`
          Sex string		`json:"sex"`
          Age int				`json:"age"`
          Height int		`json:"height"`
          Weight int		`json:"weight"`
  }
```

Go言語の `json` 標準パッケージで構造体をJSONに変換すると、フィールド名がTagで定義された名前に置換されます。

```go
people := People{Name: "太郎", Sex: "男"}
jsonBytes, _ := json.Marshal(people)
fmt.Println(string(jsonBytes)) 
// JSON: {"name": "太郎", "sex": "男"}
```

### その仕組み

`json`パッケージの内部で下記のようにタグ情報を取得してJSONデータに変換しています。

```go
 t := reflect.TypeOf(people)
 for i := 0; i < t.NumField(); i++ {
     field := t.Field(i)
     tag := field.Tag.Get("json") //  `json`タグ情報を取得
 }
```

下記のように、複数のタグは**スペース**で区切って記述できます。

```go
  type People struct {
          Name string		`json:"name" validate:"required"`
          Sex string		`json:"sex" validate:"required"`
          Age int				`json:"age" validate:"required"`
          Height int		`json:"height" validate:"required"`
          Weight int		`json:"weight" validate:"required"`
  }
```



## 埋め込みフィールド（Embedded fields）

**型だけで宣言**されていて、明示的なフィールド名がないフィールドを埋め込みフィールドと呼びます。

![image-20200801172005624.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170309/9f1923fd-4b39-72e2-fb6d-2b441836b11a.png)


Go言語は埋め込みで他言語のクラスの**継承ぽいこと**ができます。

埋め込み子構造体（`Address`）のフィールドが昇格し、構造体の外（`Person`）からダイレクトにアクセスすることが可能です。

```go
person.Adress.city → person.city
person.Adress.state → person.state
// PersonがAdressを継承しているように、Adressのフィールドをダイレクトにアクセスすることが可能となります。
```

`Person` に同名のフィールドやメソッドがあるときは、`Address` にあるそのフィールドは昇格されないです。

![image-20200801173432397.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170309/dc908a01-4998-b4bb-bc9d-1c8cdcba432f.png)



## 構造体の比較

`reflect.DeepEqual` はポインターか否かにかかわらず、

フィールドの値が同じであれば  `true` を返します。

```go
import (
    "reflect"
)

...
people1 := People{Name: "太郎"}
people2 := People{Name: "太郎"}

println(reflect.DeepEqual(people1, people2))     // true
println(reflect.DeepEqual(&people1, &people2))   // true
println(people1 == people2)    // true
println(&people1 == &people2)  // false
```

# 動画（YouTube）

https://www.youtube.com/watch?v=91_7hWkByv4

# 元記事
https://aifan.jp/2020/08/01/Go%E8%A8%80%E8%AA%9E-%E5%88%9D%E5%BF%83%E8%80%85%E5%BF%85%E8%A6%8B%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E6%A7%8B%E9%80%A0%E4%BD%93%EF%BC%88Structs%EF%BC%89/