Goでは構造体を == で比較できるらしい。
package main import ( "fmt" "unsafe" ) type Hoge struct { t bool d int s string } func main() { a := Hoge{t: true, d: 1, s:"nyan"} b := Hoge{t: true, d: 1, s:"nyan"} c := a // 値コピー d := &a // ポインタコピー fmt.Println(a == b) // true 値の比較、同値 fmt.Println(&a == &b) // false ポインタの比較、同一ではない fmt.Println(a == c) // true 値がコピーされたので同値 fmt.Println(&a == &c) // false 値がコピーされたので同一ではない fmt.Println(a == *d) // true ポインタのコピーなので同値 fmt.Println(&a == d) // true ポインタのコピーなので同一 }
どうせ同一性の比較しかできんのだろうと思っていたら、別のインスタンスでも、フィールドの値を比較して同値と判断してくれるようだ。
fmt.Println(unsafe.Sizeof(a)) // 16 fmt.Println(unsafe.Sizeof(a.t)) // 1 fmt.Println(unsafe.Sizeof(a.d)) // 4 fmt.Println(unsafe.Sizeof(a.s)) // 8 fmt.Println(unsafe.Offsetof(a.t)) // 0 fmt.Println(unsafe.Offsetof(a.d)) // 4 fmt.Println(unsafe.Offsetof(a.s)) // 8
Go playgroundではbool型の後ろに3byteパディングがある(どうやら32bit環境らしいw)
どう比較しているのだろう? フィールドを一つ一つ比較するのはコストが高そうなので、構造体の開始部分から16byteのメモリの中身を丸ごと比較してるのだろうか? だとするとパディング部分も初期化時に0埋めされてないと比較できない。まあ、セキュリティ的な観点からみるとパディングも0埋め初期化するのが正解だが…、ドキュメントを見てもよくわからん。
構造体の比較は便利そうではあるが、ポインタをフィールドに含む場合を想定すると面倒だし、スライスを持っているとそもそも比較できない。そういった面倒ごとを考慮せず同値比較するならば、reflect.DeepEqualを使うといいらしい
fmt.Println(reflect.DeepEqual(&a, &b)) // true