DeepEqual неверен после сериализации карты в gob - PullRequest
0 голосов
/ 06 июня 2018

Я столкнулся с каким-то странным поведением с отражением.У меня есть объект типа 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 обрабатывает пустые срезы;это странное взаимодействие между сериализацией и гобом.

1 Ответ

0 голосов
/ 06 июня 2018

Это связано с тем, что вы кодируете пустой фрагмент, а во время декодирования пакет encoding/gob выделяет фрагмент только в том случае, если предоставленный фрагмент (цель для декодирования) недостаточно велик для размещения закодированных значений.,Это задокументировано по адресу: gob: Типы и значения:

Обычно, если требуется выделение, декодер выделяет память.В противном случае он будет обновлять переменные назначения значениями, считанными из потока.

Поскольку закодировано 0 элементов, а срез nil может вместить 0 элементов, срез не будетвыделены.Мы можем проверить это, если напечатаем результат сравнения срезов в nil:

fmt.Println(m0["apple"] == nil, m2["apple"] == nil)

Вывод вышеизложенного (попробуйте на Go Playground ):

true false

Обратите внимание, что пакет fmt печатает значения nil срезов и пустые срезы одинаково: как и [], вы не можете полагаться на его вывод, чтобы судить, является ли срез nil или нет.

И reflect.DeepEqual() обрабатывает срез nil и пустой, но не nil срез, различающиеся (не глубокие равные):

Обратите внимание, что ненулевой пустой слайс и нулевой слайс (например, [] byte {} и [] byte (nil)) не являются глубоко равными.

...