実用Go言語 第3章「構造体」まとめ

この章では、Go言語の**構造体**（struct）について、基本的な使い方からメソッド定義、タグ、設計思想まで幅広く解説されています。Goらしい構造体設計の考え方を理解するうえで非常に重要な章です。

## 3.1 構造体の基本的な使い方

- フィールドを持つデータ構造を定義
- 大文字で始まるフィールドはエクスポートされ、外部パッケージからアクセス可能

```go
type User struct {
    ID   int
    Name string
}
```

## 3.2 構造体をインスタンス化する3つの方法

### 3.2 構造体をインスタンス化する3つの方法

Goでは構造体のインスタンスを以下の3通りで生成できます。

#### ① `new()` 関数（ポインタで返る）

```go
type User struct {
    Name string
    Age  int
}

u := new(User)
u.Name = "Taro"
u.Age = 20
```
- `*User` 型が返る（ゼロ値で初期化された構造体のポインタ）

#### ② `var` 変数宣言（値として生成）

```go
var u User
u.Name = "Taro"
u.Age = 20
```
- 値型で生成され、フィールドは**ゼロ値で初期化**
- `&u` とすればポインタとしても扱える

#### ③ 複合リテラル（コンポジットリテラル）

```go
u := User{Name: "Taro", Age: 20}       // 値型
up := &User{Name: "Taro", Age: 20}     // ポインタ型
```

- 最も一般的で、**初期値の指定が簡潔**
- 値型にもポインタ型にもできて柔軟

用途に応じて：
- **ポインタ操作したいとき** → `new()` or `&構造体{...}`
- **単純な構造体生成** → `User{}` や `var`

この3パターンを使い分けることで、**柔軟で意図の明確な構造体の初期化**が可能になります。


### 3.2.1 ファクトリー関数

```go
func NewUser(id int, name string) *User {
    return &User{ID: id, Name: name}
}
```
- 「New」を最初につけるのが一般的

## 3.3 構造体にメソッドを定義する

```go
func (u *User) Rename(newName string) {
    u.Name = newName
}
```
- レシーバーを持つ関数としてメソッドを定義

### 3.3.1 値レシーバーとポインタレシーバーの選択

Goではメソッドのレシーバーに**値**（Value）と**ポインタ**（Pointer）のどちらを使うかで、挙動と目的が変わります。

#### 値レシーバー（`func (u User) Xxx()`）

- **コピー**された構造体を受け取る
- 元の構造体には影響を与えない（＝イミュータブル）
- 小さい構造体（数フィールド程度）や、読み取り専用処理に適している

```go
func (u User) Display() {
    fmt.Println("Name:", u.Name)
}
```

#### ポインタレシーバー（`func (u *User) Xxx()`）

- **元の構造体を参照**する（＝ミュータブル）
- フィールドを書き換える処理や、大きな構造体に向いている
- 値コピーを避けられるため**パフォーマンスが良い**

```go
func (u *User) SetName(name string) {
    u.Name = name
}
```

### 3.3.2 レシーバーがnilでもメソッドは呼べる
Goでは、**ポインタレシーバーのメソッドは、レシーバーが`nil`でも呼び出せる**設計になっています。  
ただし、そのメソッドの中で**nilにアクセスするとpanic**になります。

#### 例：nilでも呼べるが、注意が必要

```go
type User struct {
    Name string
}

func (u *User) Greet() {
    if u == nil {
        fmt.Println("nil user")
        return
    }
    fmt.Println("Hello,", u.Name)
}
```

```go
var u *User = nil
u.Greet() // → panicにならず "nil user" と表示される
```

#### なぜ nil チェックが一般的か？

- レシーバーが `nil` かどうかは**メソッド内でしか判断できない**
- 多くの場面では、**nilならメソッドを呼ばない方が安全**
- うっかり `nil` のレシーバーでメソッドを呼び、**中で別のフィールドにアクセスしてpanic**…という事故が防げる

#### 実務上のベストプラクティス

- **事前にnilチェックする**

```go
if user != nil {
    user.Greet()
}
```

- または、**メソッド内で明示的に`nil`チェック**を入れる（防御的設計）


このように、「nilでもメソッドは呼べる」はGoの柔軟な仕様ですが、**呼ぶ側・実装側どちらかで対策しないと予期せぬpanicに繋がる**ため注意が必要です。


### 3.3.3 メソッドを関数型として抽出（sqlx.DBとnet/http連携）

構造体に `*sqlx.DB` を持たせ、その構造体メソッドで `Get()` を実行
#### 構造体とDBアクセスの定義

```go
type User struct {
    ID   int    `db:"id"`
    Name string `db:"name"`
}

type Server struct {
    DB *sqlx.DB
}

func (s *Server) GetUserHandler(w http.ResponseWriter, r *http.Request) {
    var user User
    err := s.DB.Get(&user, "SELECT id, name FROM users WHERE id = ?", 1)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    fmt.Fprintf(w, "User: %s (ID: %d)", user.Name, user.ID)
}
```

### 3.3.4 クロージャを使った再現

Goでは**クロージャ**（関数内で定義された関数）を使って、  
メソッドのような挙動を関数として再現することができます。

#### 例：ユーザー名を更新する関数をクロージャで定義

```go
type User struct {
    Name string
}

func main() {
    user := &User{Name: "Taro"}

    // クロージャ：userを囲い込んで関数化
    rename := func(newName string) {
        user.Name = newName
    }

    rename("Jiro")

    fmt.Println(user.Name) // → "Jiro"
}
```

#### 🔍 ポイント

- `rename` は `user` という変数を**外からキャプチャして保持**している
- `func(string)` の形をしているが、**内部状態（user）に作用できる**
- これは**レシーバー付きメソッドとほぼ同じような振る舞い**を、関数単体で実現する方法


このように、**構造体や外部状態に作用する小さな処理**を手軽に関数化したい場合、  
クロージャは非常に便利な手段となります。


### 3.3.5 ジェネリクスとメソッド
Go 1.18 以降では、**型パラメータ（ジェネリクス）付きの構造体**にも、通常の構造体と同様に**メソッドを定義できます**。

#### 例：数値型に対応した汎用ラッパー構造体

```go
package main

import (
    "fmt"
)

// 数値をラップするジェネリクス構造体
type Wrapper[T int | float64] struct {
    Value T
}

// メソッド：2倍にして返す
func (w Wrapper[T]) Double() T {
    return w.Value * 2
}
```

#### 利用例：

```go
func main() {
    i := Wrapper[int]{Value: 10}
    f := Wrapper[float64]{Value: 3.4}

    fmt.Println(i.Double()) // 20
    fmt.Println(f.Double()) // 6.8
}
```

#### ポイント

- `Wrapper[T]` のように、**型パラメータ付き構造体に対してもメソッドが定義可能**
- 通常の構造体と同じ感覚で扱えるが、**型ごとの動作を1つの定義に集約**できるのが強み

Goのジェネリクスは、**メソッド定義・インターフェース実装・制約付きの操作**にも対応しているため、  再利用性の高い抽象的なコードが安全に書けるようになっています。


## 3.4 構造体の埋め込みで共通部分を使いまわす

- 匿名フィールドにより、埋め込まれた型のメソッドを外部から呼び出せる

```go
type Address struct {
    City string
}

type Employee struct {
    Name string
    Address
}
```

## 3.5 タグを使って構造体にメタデータを埋め込む

### 3.5.1 タグの記法

```go
type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}
```

### 3.5.2 データの書き込み（reflect を使用）

`reflect` を使って構造体のフィールド情報を動的に取得し、簡易的に JSON 風の文字列に変換する例です。

```go
package main

import (
    "fmt"
    "reflect"
)

type User struct {
    ID   int
    Name string
}

func main() {
    user := User{ID: 1, Name: "Taro"}
    v := reflect.ValueOf(user)
    t := reflect.TypeOf(user)

    fmt.Print("{")
    for i := 0; i < v.NumField(); i++ {
        if i > 0 {
            fmt.Print(", ")
        }
        fmt.Printf(`"%s":`, t.Field(i).Name)
        fmt.Printf(`"%v"`, v.Field(i).Interface())
    }
    fmt.Println("}")
}
```

### 3.5.3 データの読み込み（reflect を使用）

文字列から `map[string]string` に分解して、`reflect` を使って構造体に動的に値を設定する簡易例です。

```go
package main

import (
    "fmt"
    "reflect"
)

type User struct {
    ID   int
    Name string
}

func main() {
    data := map[string]string{
        "ID":   "1",
        "Name": "Taro",
    }

    var user User
    v := reflect.ValueOf(&user).Elem()

    for key, val := range data {
        f := v.FieldByName(key)
        if f.IsValid() && f.CanSet() {
            switch f.Kind() {
            case reflect.Int:
                var i int
                fmt.Sscanf(val, "%d", &i)
                f.SetInt(int64(i))
            case reflect.String:
                f.SetString(val)
            }
        }
    }

    fmt.Printf("%+v\n", user) // {ID:1 Name:Taro}
}
```

#### 補足

- 通常は `encoding/json` パッケージを使えば簡単に済む処理ですが、`reflect` を使うことで「**動的に型やフィールドを扱う処理**」を実現できます。
- 実際の用途では、**カスタムデシリアライズや汎用フォームバインド処理**などで使われることがあります。


## 3.6 構造体を設計するポイント

### 3.6.1 ポインタ型で扱うケース
構造体の内部に **スライス、map、ポインターなどの参照型フィールド**を持つ場合、  
その構造体は基本的に **`Struct`（ポインタ型）として扱う**ように設計します。

#### 理由と利点

- **フィールドの変更が正しく反映される**

  値型で扱うと構造体自体がコピーされ、参照型フィールドは共有されても**構造体の更新が意図通りに働かない**ことがあります。


### 3.6.2 値として扱える場合
- 小さくてイミュータブルなデータ

### 3.6.3 ミュータブル vs イミュータブル
Goでは、構造体の扱いを**ミュータブル（変更可能）にするか、イミュータブル（変更不可）にするか**は設計上の選択です。  

#### エンティティはミュータブルに設計する

**エンティティ**（User, Order などドメイン上の主要構造体）は、  
「状態が変化する」ことが前提のため、**ミュータブル**（ポインタ型＋更新メソッド）にするのが自然です。


### 3.6.4 ゼロ値の動作を保証する
- 「ゼロ値でも動く」は便利だが、**すべてに適用すべきとは限らない**
- **外部依存や初期化が必要な構造体は、ファクトリー関数での生成に限定**する方が安全で明確
- ファクトリー関数を使うことで、**構造体の不変条件や依存注入**を強制できる

### 3.6.5 実装方法の選択ポイント
- ポインターで扱うのが前提
- ミュータブルなAPIセットを提供
- 特定のファクトリー関数のみ動作（ゼロ値動作を保証しない）

## 3.7 空の構造体を使ってゴルーチン間での通知を行う

Goでは `struct{}`（空の構造体）を使うことで、**「何かが起きた」ことだけを伝える最小限の通知**が可能です。

`struct{}` は**フィールドを持たず、サイズも0バイト**のため、  
**通知専用のチャンネルやフラグ用途として非常に効率的**に使えます。

#### 基本的な使い方（ゴルーチン間の完了通知）

```go
done := make(chan struct{})

go func() {
    // 処理
    done <- struct{}{} // 完了を通知
}()

<-done // 処理完了を待つ
```
#### 主な用途

- ゴルーチン間の「完了通知」に使う（`chan struct{}`）
- **「何かが起きた」ことだけを伝える**用途に最適
- **map[struct{}]bool** にして、他言語における「集合（Set）」のように使う
- **状態を持たないメソッド集**として使う

## 3.8 構造体のメモリ割り当てを高速化する

構造体のインスタンスを大量に生成・破棄する場面では、  
**`sync.Pool` を使って使いまわすことで、メモリ割り当てのオーバーヘッドを削減**できます。

#### `sync.Pool` の活用

- **使い終わった構造体をプールに戻し、再利用**
- 毎回 `new()` で確保せずに済むため、**GC負荷を大幅に軽減可能**

---

#### 例：User構造体を `sync.Pool` で管理

```go
package main

import (
    "fmt"
    "sync"
)

type User struct {
    ID   int
    Name string
}

var userPool = sync.Pool{
    New: func() any {
        return &User{}
    },
}

func main() {
    // プールから取得（なければ New が呼ばれる）
    u := userPool.Get().(*User)

    u.ID = 1
    u.Name = "Taro"

    fmt.Printf("User: %+v\n", u)

    // 使用後はプールに戻す
    userPool.Put(u)
}
```

#### 利点

- **構造体の割り当てコストが大きいケース**（例：リクエストごとの一時データ）で有効
- `sync.Pool` は**GCサイクル間で生存しているオブジェクトを再利用**するため、  
  短命オブジェクトのGC対象を減らせる

このように `sync.Pool` を使うと、**構造体の生成コスト削減とメモリ効率改善**が同時に実現できます。  

## 3.9 構造体とオブジェクト指向の違いを知る

### 3.9.1 構造体の用途
- ライブラリの機能を呼び出すメソッドを集約したインスタンス
- インターフェイス経由で他から呼び出されるエントリーポイント
- データセットを持ち運ぶためのバリューオブジェクト
- タプルと呼ばれるようなデータの塊

### 3.9.2 埋め込みは継承ではない
Goの構造体埋め込みは**継承ではなく、あくまで「フィールドとしてぶら下がっている」だけの関係**です。

- 埋め込んだ構造体は **親ではなく子（部品）**
- 同名のメソッドを定義しても**オーバーライドはできない**

#### 例：埋め込みでメソッドは上書きできない

```go
type Base struct{}

func (b Base) Hello() {
    fmt.Println("Hello from Base")
}

type Child struct {
    Base
}

func (c Child) Hello() {
    fmt.Println("Hello from Child")
}

func main() {
    c := Child{}
    c.Hello()     // → Hello from Child
    c.Base.Hello() // → Hello from Base
}
```

#### ポイント

- `Base.Hello()` は明示的に呼べてしまう → **隠れていない**
- `Child.Hello()` は別定義であり、**継承やオーバーライドとは異なる**
- **委譲的な使い方**として、メソッドの呼び出し先を切り替える目的に使われる

このように、Goの埋め込みは「継承」ではなく、「**部品を組み合わせる**」という考え方に基づいています。

### 3.9.3 ストラテジーパターン的設計
Goでは、共通の処理の一部を差し替えたい場合、  
**テンプレートメソッドパターンのような継承ベースの設計ではなく、ストラテジーパターンを取るのが一般的**です。

#### ストラテジーパターンとは？

- 振る舞い（戦略）を構造体または関数として**明示的に分離**
- 必要な実装を**依存注入**（DI）で差し替えることで、柔軟に処理を切り替える

#### なぜテンプレートメソッドではないのか？

- Goには継承がないため、「一部の処理だけを上書きする」というテンプレートメソッドパターンは**適用できない**
- 埋め込みで似たことはできるが、**オーバーライドできないため制限がある**

### 3.9.4 あえてオーバーライドを実装する
Goには継承によるオーバーライドの仕組みはありませんが、  
1番目の引数としてインスタンスのポインターを渡し、インターフェイスで受けることで、他のオブジェクト思考言語と同等のオーバーライドが実現できる

## ✅ まとめ

Goにおける構造体は、OOPのように見えても実は**委譲・明示的設計**が本質です。  
柔軟かつ安全な構造体の設計には以下のポイントを意識しましょう：

- 値 vs ポインタ
- 構造体の埋め込み活用
- タグによるメタ情報管理
- ゼロ値の安全設計
- オブジェクト指向とは異なるGoらしい抽象化