Go言語のソースコードを見たとき、[]
と {}
の使い分けに戸惑ったことはありません?わたしは、めっちゃアリます!他の言語とスイッチする時に混乱しやすいため、備忘録としてまとめました。
角括弧 []
の役割と具体例
1. コレクション型の定義
// 配列(固定長): [要素数]型
var array [3]int // [0, 0, 0]
// スライス(可変長): []型
var slice []string // nilスライス
// マップのキー型指定: map[キー型]値型
var m map[string]int // nilマップ
2. 要素アクセスとスライス操作
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. バッファ付きチャネル
ch := make(chan int, 10) // バッファサイズ10
波括弧 {}
の役割と具体例
1. 複合リテラル(初期化)
// スライス初期化
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. ブロック定義
// if文の条件ブロック
if count > 5 {
fmt.Println("Threshold exceeded")
}
// 関数本体
func calculate(a, b int) int {
return a * b
}
// 無名関数
func() {
fmt.Println("Immediate execution")
}()
[]
と {}
の組み合わせパターン
スライスの宣言と初期化
// 型推論を利用
fibonacci := []int{0, 1, 1, 2, 3, 5}
// 多次元スライス
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
マップの動的初期化
// マップの宣言と初期化
config := map[string]string{
"env": "production",
"port": "8080",
}
// 後から要素追加
config["timeout"] = "30s"
構造体スライスの初期化
type Point struct{ X, Y int }
// 複合リテラルで一括初期化
points := []Point{
{X: 1, Y: 2},
{X: 3, Y: 4},
{X: 5, Y: 6},
}
実践的ユースケース
ケース1: JSONデータの処理
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: エラーハンドリング
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: 並行処理でのマップ使用
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: マップ初期化の構文ミス
// 誤り: マップに[]を使用
m := map[string]int["a":1, "b":2]
// 正しい方法
m := map[string]int{"a":1, "b":2}
エラー2: 構造体初期化の型不一致
type Config struct {
Timeout int
}
// 誤り: 文字列をintフィールドに代入
c := Config{Timeout: "30"}
// 正しい方法
c := Config{Timeout: 30}
エラー3: スライスの範囲外アクセス
s := []int{10, 20, 30}
// パニック発生
fmt.Println(s[5])
// 安全なアクセス方法
if len(s) > 5 {
fmt.Println(s[5])
}
ベストプラクティス
-
複合リテラルのフォーマット規則
// 複数行の場合 profile := map[string]string{ "name": "Alice", "email": "alice@example.com", "country": "JP", // 末尾カンマ必須 }
-
makeを使ったスライス初期化
// キャパシティ指定でメモリ割当最適化 ids := make([]int, 0, 100) // 長さ0, 容量100
-
マップの存在チェック
if value, exists := configMap["timeout"]; exists { // キーが存在する場合の処理 }
-
構造体初期化時のフィールド明示
// フィールド名明示(順番変更に強い) u := User{ ID: 101, Name: "Bob", }
Go言語の設計思想から見る括弧の意味
Go言語の括弧の使い分けには、言語設計の核となる原則が反映されています:
-
明示性:
[]
は型定義、{}
は初期化とブロック - 簡潔性: 複合リテラル構文による直感的な初期化
- 型安全性: コンパイル時の厳密なチェック
- 実用性: スライス操作など日常的な操作の最適化
「Goの構文は小さな言語を意図して設計されている。すべての構文要素は明確な目的を持ち、一貫した方法で動作する」
- Go言語設計者 Rob Pike
まとめ
Go言語の []
と {}
の使い分けをマスターすることは、Goのコアコンセプトを理解する上で不可欠です:
-
[]
は 型定義・要素アクセス・スライス操作 に使用 -
{}
は 初期化・ブロック定義 に使用 - 組み合わせて 複合データ構造 を効率的に操作
- 他言語との違いを理解して マイグレーション時に混乱を回避