https://blog.gainings.dev/posts/go_deepequal_trap/

はじめに

この記事は 2018年 アドベントカレンダーとなんの関係もない記事です.

どうも一回も怒られたことがないバイトリーダーのゲインです.

さて本日バイトでModel周りのテストコードを書いていた時に reflect.DeepEqual でガッツリハマったので,共有しておきたいと思います.

ハマったこと

さて皆さんこちらのコードを御覧ください. playgroundはこちら

このコードの実行結果はどうなるでしょう. 実行せずに考えてみてください.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"reflect"
)

type Hoge struct {
	Hoge string `json:"hoge"`
	Huga int    `json:"huga"`
}

func main() {
	m1 := make(map[string]interface{}, 0)
	m1["hoge"] = "hogehoge"
	m1["huga"] = 1

	m2 := make(map[string]interface{}, 0)
	m2["hoge"] = "hogehoge"
	m2["huga"] = 1

	hoge := Hoge{
		Hoge: "hogehoge",
		Huga: 1,
	}
	hogeJSON, err := json.Marshal(hoge)
	if err != nil {
		log.Fatal(err)
	}
	var m3 map[string]interface{}
	err = json.Unmarshal(hogeJSON, &m3)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("m1 == m2?: %t\\n", reflect.DeepEqual(m1, m2))
	fmt.Printf("m1 == m3?: %t\\n", reflect.DeepEqual(m1, m3))
	fmt.Printf("m2 == m3?: %t\\n", reflect.DeepEqual(m2, m3))
}

中身としてはm1,m2は同じキーに対して同じ値を入れた別の変数.

m3に関しては構造体を一度JSONにしてから map[string]interface{} にUnmarshalした変数になってます.

さてこれを実行すると以下のように表示されるでいいですか?

m1==m2?: true
m1==m3?: true
m2==m3?: true

…残念ながら違うんです.

午前中の私は上記になると思ってました.

実際はこうです.

m1 == m2?: true
m1 == m3?: false
m2 == m3?: false

さてどうしてこんなことになるんでしょう.

中身を確認してみましょう