DeepEqual неверен после сериализации карты в каплю

Я столкнулся с некоторым странным поведением с 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.

Предполагаю, что это как-то связано с тем, что фрагмент nil и пустой фрагмент обрабатываются одинаково во многих местах, но, возможно, не внутри DeepEqual.

Adrian 06.06.2018 17:41
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
2
1
225
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это связано с тем, что вы кодируете пустой срез, и во время декодирования пакет 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 06.06.2018 17:56

@rampatowl gob не обрабатывает nil и пустые слайсы одинаково, но не выделяет слайс, если «существующего» достаточно для хранения закодированных элементов. Если это проблема для вас, используйте слайсы nil вместо пустых перед кодированием. Я не думаю, что в них есть недостатки. gob делает это для минимизации выделения (и, следовательно, повышения производительности).

icza 06.06.2018 17:57

Попался, спасибо. Я удивлен, что кодировка работает, когда значением карты является nil, потому что в случае, когда тип значения является указателем, это дает мне ошибку gob: encodeReflectValue: nil element. Если у вас есть какое-либо представление об этом противоречии, я разместил здесь отдельный вопрос: stackoverflow.com/questions/50725102/…

rampatowl 06.06.2018 18:19

Другие вопросы по теме