Go言語における `[]` と `{}` の使い分け

Go言語のソースコードを見たとき、`[]` と `{}` の使い分けに戸惑ったことはありません？わたしは、めっちゃアリます！他の言語とスイッチする時に混乱しやすいため、備忘録としてまとめました。　


### 角括弧 `[]` の役割と具体例

#### 1. コレクション型の定義
```go
// 配列（固定長）: [要素数]型
var array [3]int  // [0, 0, 0]

// スライス（可変長）: []型
var slice []string // nilスライス

// マップのキー型指定: map[キー型]値型
var m map[string]int // nilマップ
```

#### 2. 要素アクセスとスライス操作
```go
arr := [5]int{10, 20, 30, 40, 50}

// 要素アクセス
fmt.Println(arr[2]) // 30

// スライス操作（半開区間）
slice := arr[1:4] // [20, 30, 40]

// 容量と長さの取得
fmt.Println(len(slice), cap(slice)) // 3, 4
```

#### 3. バッファ付きチャネル
```go
ch := make(chan int, 10) // バッファサイズ10
```

### 波括弧 `{}` の役割と具体例

#### 1. 複合リテラル（初期化）
```go
// スライス初期化
names := []string{"Alice", "Bob", "Charlie"}

// マップ初期化
scores := map[string]int{
    "Math":    90,
    "Science": 85, // 末尾カンマ必須
}

// 構造体初期化
type User struct {
    ID   int
    Name string
}
user := User{ID: 1, Name: "Alice"}
```

#### 2. ブロック定義
```go
// if文の条件ブロック
if count > 5 {
    fmt.Println("Threshold exceeded")
}

// 関数本体
func calculate(a, b int) int {
    return a * b
}

// 無名関数
func() {
    fmt.Println("Immediate execution")
}()
```

### `[]` と `{}` の組み合わせパターン

#### スライスの宣言と初期化
```go
// 型推論を利用
fibonacci := []int{0, 1, 1, 2, 3, 5}

// 多次元スライス
matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}
```

#### マップの動的初期化
```go
// マップの宣言と初期化
config := map[string]string{
    "env":  "production",
    "port": "8080",
}

// 後から要素追加
config["timeout"] = "30s"
```

#### 構造体スライスの初期化
```go
type Point struct{ X, Y int }

// 複合リテラルで一括初期化
points := []Point{
    {X: 1, Y: 2},
    {X: 3, Y: 4},
    {X: 5, Y: 6},
}
```

### 実践的ユースケース

#### ケース1: JSONデータの処理
```go
import "encoding/json"

type Product struct {
    ID    int      `json:"id"`
    Name  string   `json:"name"`
    Tags  []string `json:"tags"`
}

// JSON → Go構造体
jsonData := []byte(`{"id":101,"name":"Laptop","tags":["tech","electronics"]}`)

var p Product
json.Unmarshal(jsonData, &p)

fmt.Println(p.Tags[1]) // "electronics"
```

#### ケース2: エラーハンドリング
```go
if err := process(); err != nil {
    // エラー型による分岐
    switch e := err.(type) {
    case *NetworkError:
        log.Printf("Network error: %v", e)
    case *ValidationError:
        log.Printf("Validation failed: %v", e)
    default:
        log.Printf("Unexpected error: %v", err)
    }
}
```

#### ケース3: 並行処理でのマップ使用
```go
var (
    mu    sync.Mutex
    cache = make(map[string]time.Time)
)

func updateCache(key string) {
    mu.Lock()
    defer mu.Unlock()
    cache[key] = time.Now() // 排他制御下でのマップ更新
}
```

### 他言語との比較表

| 機能               | Go言語                  | Python                | JavaScript/TypeScript |
|--------------------|-------------------------|-----------------------|-----------------------|
| **配列/リスト**    | `[n]T`, `[]T`           | `list = [1,2,3]`      | `const arr: number[]` |
| **マップ/辞書**    | `map[K]V{}`             | `dict = {"key":value}`| `const obj: Record<K,V>` |
| **構造体/クラス**  | `struct{...}{}`         | クラス`__init__`      | インターフェース/クラス |
| **ブロック**       | `{ ... }`               | インデント            | `{ ... }`             |
| **スライス操作**   | `slice[1:4]`            | `list[1:4]`           | `arr.slice(1,4)`      |

### よくある間違いと解決策

#### エラー1: マップ初期化の構文ミス
```go
// 誤り: マップに[]を使用
m := map[string]int["a":1, "b":2] 

// 正しい方法
m := map[string]int{"a":1, "b":2}
```

#### エラー2: 構造体初期化の型不一致
```go
type Config struct {
    Timeout int
}

// 誤り: 文字列をintフィールドに代入
c := Config{Timeout: "30"} 

// 正しい方法
c := Config{Timeout: 30}
```

#### エラー3: スライスの範囲外アクセス
```go
s := []int{10, 20, 30}

// パニック発生
fmt.Println(s[5]) 

// 安全なアクセス方法
if len(s) > 5 {
    fmt.Println(s[5])
}
```

### ベストプラクティス

1. **複合リテラルのフォーマット規則**
   ```go
   // 複数行の場合
   profile := map[string]string{
       "name":    "Alice",
       "email":   "alice@example.com",
       "country": "JP", // 末尾カンマ必須
   }
   ```

2. **makeを使ったスライス初期化**
   ```go
   // キャパシティ指定でメモリ割当最適化
   ids := make([]int, 0, 100) // 長さ0, 容量100
   ```

3. **マップの存在チェック**
   ```go
   if value, exists := configMap["timeout"]; exists {
       // キーが存在する場合の処理
   }
   ```

4. **構造体初期化時のフィールド明示**
   ```go
   // フィールド名明示（順番変更に強い）
   u := User{
       ID:   101,
       Name: "Bob",
   }
   ```

### Go言語の設計思想から見る括弧の意味

Go言語の括弧の使い分けには、言語設計の核となる原則が反映されています：

- **明示性**: `[]` は型定義、`{}` は初期化とブロック
- **簡潔性**: 複合リテラル構文による直感的な初期化
- **型安全性**: コンパイル時の厳密なチェック
- **実用性**: スライス操作など日常的な操作の最適化

> 「Goの構文は小さな言語を意図して設計されている。すべての構文要素は明確な目的を持ち、一貫した方法で動作する」  
> \- Go言語設計者 Rob Pike

### まとめ

Go言語の `[]` と `{}` の使い分けをマスターすることは、Goのコアコンセプトを理解する上で不可欠です：

1. `[]` は **型定義・要素アクセス・スライス操作** に使用
2. `{}` は **初期化・ブロック定義** に使用
3. 組み合わせて **複合データ構造** を効率的に操作
4. 他言語との違いを理解して **マイグレーション時に混乱を回避**