DeepEqual неверен после сериализации карты в gob
Я столкнулся с каким-то странным поведением с отражением. У меня есть объект типа map[string][]string
с одним ключом, значение которого является пустым срезом. Когда я использую gob для кодирования этого объекта, а затем декодирую его в другую карту, эти две карты не равны в соответствии с отражением.DeepEqual (даже если содержимое идентично).
package main
import (
"fmt"
"bytes"
"encoding/gob"
"reflect"
)
func main() {
m0 := make(map[string][]string)
m0["apple"] = []string{}
// Encode m0 to bytes
var network bytes.Buffer
enc := gob.NewEncoder(&network)
enc.Encode(m0)
// Decode bytes into a new map m2
dec := gob.NewDecoder(&network)
m2 := make(map[string][]string)
dec.Decode(&m2)
fmt.Printf("%t\n", reflect.DeepEqual(m0, m2)) // false
fmt.Printf("m0: %+v != m2: %+v\n", m0, m2) // they look equal to me!
}
Выход:
false
m0: map[apple:[]] != m2: map[apple:[]]
Пара замечаний из последующих экспериментов:
Если я сделаю ценность m0["apple"]
например, непустой срез m0["apple"] = []string{"pear"}
, тогда DeepEqual возвращает истину.
Если я сохраню значение как пустой фрагмент, но создаю идентичную карту с нуля, а не с использованием gob, тогда DeepEqual возвращает true:
m1 := make(map[string][]string)
m1["apple"] = []string{}
fmt.Printf("%t\n", reflect.DeepEqual(m0, m1)) // true!
Так что не проблема в том, как DeepEqual обрабатывает пустые фрагменты; это странное взаимодействие между этим и сериализацией Гоба.
1 ответ
Это потому, что вы кодируете пустой фрагмент, и во время декодирования encoding/gob
Пакет выделяет фрагмент только в том случае, если предоставленный фрагмент (цель для декодирования) недостаточно велик для размещения закодированных значений. Это задокументировано по адресу: gob: Типы и значения:
В общем случае, если требуется выделение, декодер выделит память. Если нет, он обновит переменные назначения значениями, считанными из потока.
Поскольку там закодировано 0 элементов, а nil
Срез идеально подходит для размещения 0 элементов, срез не будет выделен. Мы можем проверить это, если напечатаем результат сравнения срезов в nil
:
fmt.Println(m0["apple"] == nil, m2["apple"] == nil)
Вывод вышесказанного (попробуйте на игровой площадке Go):
true false
Обратите внимание, что fmt
печать на упаковке nil
значения срезов и пустые срезы аналогично: как []
, вы не можете полагаться на его вывод, чтобы судить, является ли срезы nil
или нет.
А также reflect.DeepEqual()
лечит nil
срез и пустой, но не nil
срез разные (неглубокие равные):
Обратите внимание, что ненулевой пустой слайс и нулевой слайс (например, []byte{} и []byte(nil)) не являются глубоко равными.