Я столкнулся с некоторым странным поведением с Reflective.DeepEqual. У меня есть объект типа 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 вернет true.
Если я сохраню значение как пустой срез, но построю идентичную карту с нуля, а не с помощью gob, тогда DeepEqual вернет true:
m1 := make(map[string][]string)
m1["apple"] = []string{}
fmt.Printf("%t\n", reflect.DeepEqual(m0, m1)) // true!
Так что это не совсем проблема того, как DeepEqual обрабатывает пустые срезы; это какое-то странное взаимодействие между этим и сериализацией gob.

Это связано с тем, что вы кодируете пустой срез, и во время декодирования пакет encoding/gob выделяет срез только в том случае, если предоставленный (целевой объект для декодирования) недостаточно велик для размещения закодированных значений. Это задокументировано по адресу: gob: Типы и значения:
In general, if allocation is required, the decoder will allocate memory. If not, it will update the destination variables with values read from the stream.
Поскольку закодировано 0 элементов, а слайс nil вполне способен вместить 0 элементов, ни один слайс не будет выделен. В этом можно убедиться, если распечатать результат сравнения срезов с nil:
fmt.Println(m0["apple"] == nil, m2["apple"] == nil)
Результат вышеизложенного (попробуйте на Перейти на игровую площадку):
true false
Обратите внимание, что пакет fmt печатает значения срезов и пустые срезы nil таким же образом: как и [], вы не можете полагаться на его вывод, чтобы судить, является ли срез nil или нет.
И reflect.DeepEqual() обрабатывает срез nil и пустой, но не-nil срез по-разному (неглубоко равно):
Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.
Спасибо за объяснение! Итак, основная проблема заключается в том, что gob одинаково обрабатывает нулевые и пустые срезы, а DeepEqual - нет. Как вы думаете, для этого есть веская причина или нужно менять один из пакетов? Интересно, следует ли мне отправить проблему с git?
@rampatowl gob не обрабатывает nil и пустые слайсы одинаково, но не выделяет слайс, если «существующего» достаточно для хранения закодированных элементов. Если это проблема для вас, используйте слайсы nil вместо пустых перед кодированием. Я не думаю, что в них есть недостатки. gob делает это для минимизации выделения (и, следовательно, повышения производительности).
Попался, спасибо. Я удивлен, что кодировка работает, когда значением карты является nil, потому что в случае, когда тип значения является указателем, это дает мне ошибку gob: encodeReflectValue: nil element. Если у вас есть какое-либо представление об этом противоречии, я разместил здесь отдельный вопрос: stackoverflow.com/questions/50725102/…
Предполагаю, что это как-то связано с тем, что фрагмент
nilи пустой фрагмент обрабатываются одинаково во многих местах, но, возможно, не внутриDeepEqual.