Golangの構造体比較

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