Как сравнить struct, slice, map равны?

Я хочу проверить, что две структуры равны, но имеют некоторые проблемы:

package main

import (
"fmt"
"reflect"
)

type T struct {
    X int
    Y string
    Z []int
    M map[string]int
}

func main() {
    t1 := T{
        X:1,
        Y:"lei",
        Z:[]int{1,2,3},
        M:map[string]int{
            "a":1,
            "b":2,
        },
    }

    t2 :=  T{
        X:1,
        Y:"lei",
        Z:[]int{1,2,3},
        M:map[string]int{
            "a":1,
            "b":2,
        },
    }


    fmt.Println(t2 == t1)
    //error - invalid operation: t2 == t1 (struct containing []int cannot be compared)


    fmt.Println(reflect.ValueOf(t2) == reflect.ValueOf(t1))
    //false
    fmt.Println(reflect.TypeOf(t2) == reflect.TypeOf(t1))
    //true


    //Update: slice or map
    a1 := []int{1,2,3,4}
    a2 := []int{1,2,3,4}

    fmt.Println(a1==a2)
    //invalid operation: a1 == a2 (slice can only be compared to nil)

    m1 := map[string]int{   
        "a":1,
        "b":2,
    }
    m2 := map[string]int{   
        "a":1,
        "b":2,
    }
    fmt.Println(m1==m2)
    // m1 == m2 (map can only be compared to nil)
}

http://play.golang.org/p/AZIzW2WunI

Ответ 1

Вы можете использовать reflect.DeepEqual, или вы можете реализовать свою собственную функцию (эффективность которой будет лучше, чем использование рефлексии):

http://play.golang.org/p/CPdfsYGNy_

m1 := map[string]int{   
    "a":1,
    "b":2,
}
m2 := map[string]int{   
    "a":1,
    "b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))

Ответ 2

reflect.DeepEqual часто неправильно используется для сравнения двух одинаковых структур, как в вашем вопросе.

cmp.Equal - лучший инструмент для сравнения структур.

Чтобы понять, почему рефлексия опрометчива, давайте взглянем на документацию:

Значения структур сильно равны, если их соответствующие поля, как экспортированные, так и не экспортированные, глубоко совпадают.

....

числа, числа, строки и каналы - очень равны, если они равны, используя оператор Go ==.

Если мы сравним два значения time.Time одного и того же времени UTC, то t1 == t2 будет ложным, если их часовой пояс метаданных отличается.

go-cmp ищет метод Equal() и использует его для правильного сравнения времени.

Пример:

m1 := map[string]int{
    "a": 1,
    "b": 2,
}
m2 := map[string]int{
    "a": 1,
    "b": 2,
}
fmt.Println(cmp.Equal(m1, m2)) // will result in true

Ответ 3

Вот как бы вы применили свою собственную функцию http://play.golang.org/p/Qgw7XuLNhb

func compare(a, b T) bool {
  if &a == &b {
    return true
  }
  if a.X != b.X || a.Y != b.Y {
    return false
  }
  if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) {
    return false
  }
  for i, v := range a.Z {
    if b.Z[i] != v {
      return false
    }
  }
  for k, v := range a.M {
    if b.M[k] != v {
      return false
    }
  }
  return true
}

Ответ 4

Если вы сравниваете их в модульном тесте, удобной альтернативой является функция EqualValues в testify.