スライスやマップを含む構造体は == で比較できない
スライスやマップ、関数値をフィールドに持つ構造体は比較不可能な型として扱われ、==演算子で比較しようとするとコンパイルエラーになります。
エラーメッセージの読み方
./main.go:12:14: invalid operation: a == b (struct containing []int cannot be compared)
./main.go- ファイル名
12- 行番号
14- 列番号
invalid operation: a == b (struct containing []int cannot be compared)- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 package main 2 3 import "fmt" 4 5 type Box struct { 6 Items []int 7 } 8 9 func main() { 10 a := Box{Items: []int{1}} 11 b := Box{Items: []int{1}} 12 fmt.Println(a == b ) ^ 13 }
Itemsフィールドがスライスのため、Box型全体を==で比較することはできません。フィールドごとに比較するか、reflect.DeepEqualを使う必要があります。
直し方: a == b を len(a.Items) == len(b.Items) にします。
パターン2
1 package main 2 3 import "fmt" 4 5 type Cart struct { 6 Tags []string 7 } 8 9 func main() { 10 a := Cart{Tags: []string{"go"}} 11 b := Cart{Tags: []string{"go"}} 12 fmt.Println(a == b ) ^ 13 }
[]stringも比較不能な型のひとつで、同様に構造体全体の==比較を妨げます。
直し方: a == b を len(a.Tags) == len(b.Tags) にします。
パターン3
1 package main 2 3 import "fmt" 4 5 type Config struct { 6 Options map[string]bool 7 } 8 9 func main() { 10 a := Config{Options: map[string]bool{"x": true}} 11 b := Config{Options: map[string]bool{"x": true}} 12 fmt.Println(a == b ) ^ 13 }
マップもスライスと同様に比較演算子を持たない型で、含む構造体全体の比較を不可能にします。
直し方: a == b を len(a.Options) == len(b.Options) にします。
パターン4
1 package main 2 3 import "fmt" 4 5 type Team struct { 6 Members []string 7 } 8 9 func main() { 10 a := Team{Members: []string{"a", "b"}} 11 b := Team{Members: []string{"a", "b"}} 12 fmt.Println(a == b ) ^ 13 }
すべてのフィールドが比較可能な型であることが==成立の前提で、1つでもスライスがあれば崩れます。
直し方: a == b を len(a.Members) == len(b.Members) にします。
パターン5
1 package main 2 3 import "fmt" 4 5 type Playlist struct { 6 Songs []int 7 } 8 9 func main() { 10 a := Playlist{Songs: []int{1, 2}} 11 b := Playlist{Songs: []int{1, 2}} 12 fmt.Println(a == b ) ^ 13 }
内容が本当に一致しているか比べたい場合は、reflect.DeepEqualやフィールドごとの比較関数を自分で書く必要があります。
直し方: a == b を len(a.Songs) == len(b.Songs) にします。
よくある誤解
「構造体同士の==は、フィールドを1つずつ再帰的に比較してくれるはず」という思い込みは誤りです。==による比較はすべてのフィールドが比較可能な型であることが前提で、スライスやマップのように比較演算子を持たない型が1つでもあるとコンパイルできません。
まとめ
スライスやマップを含む構造体は == で比較できないは上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。