GoB Panics декодирования интерфейса

У меня есть структура с неэкспортированными полями, которые должны кодироваться и декодироваться.

Сказать:

type A struct {
    s int
}
func (a *A) Inc() {
    a.s++
}

Очевидно, что в этом случае мне нужно реализовать gob.GobEncoder а также gob.GobDecoder интерфейсы. И если я использую структуру напрямую, все работает нормально:

https://play.golang.org/p/dm3HwaI8eU

Но мне также нужно иметь интерфейс, который реализует ту же логику и является сериализуемым:

type Incer interface {
    gob.GobEncoder
    gob.GobDecoder
    Inc()
}

Полный код: https://play.golang.org/p/Zig2mPrnrq

И вдруг паникует

panic: interface conversion: interface is nil, not gob.GobDecoder [recovered]
    panic: interface conversion: interface is nil, not gob.GobDecoder

Но если я прокомментирую интерфейсы gob, все снова станет хорошо.

Я что-то упустил? Потому что описанное поведение кажется мне довольно странным

2 ответа

Решение

Проблема заключается в том, что Incer Интерфейс реализует специальный интерфейс, особенный для encoding/gob пакет: gob.GobDecoder,

Если твой Incer интерфейс содержит только Inc() метод, он будет работать, потому что декодер gob видит, что вы декодируете в тип интерфейса, и будет использовать переданный тип для декодирования значения и проверки во время выполнения, реализует ли декодированное значение (тип которого включен и передается в потоке) тип интерфейса назначения, и в этом случае он будет:

type Incer interface {
    Inc()
}

Так что расшифровка удалась.

Если Incer Интерфейс также встраивает gob.GobEncoder а также gob.GobDecoder интерфейсы, они по определению ответственны за кодирование / декодирование. Если тип реализует эти интерфейсы, декодер не будет пытаться декодировать значение, используя переданный тип, а вместо этого вызовет GobDecode() метод целевого значения, при необходимости создающий нулевое значение.

Так как вы проходите nil значение для Decoder.Decode() декодер должен создать нулевое значение, но он не знает, какой тип создать, поскольку передаваемое вами значение является указателем на интерфейс. Вы не можете создать значение типа интерфейса, только значение конкретного типа, которое может удовлетворять определенным интерфейсам.

Вы не можете включить gob.GobEncoder а также gob.GobDecoder в вашем Incer интерфейс. Я знаю, что вы хотите убедиться, что реализации действительно их реализуют, но тогда - как вы можете видеть - вы не сможете декодировать их в "общий" Incer значение интерфейса Кроме того, я даже не вижу необходимости включать их в Incer: gob.GobEncoder а также gob.GobDecoder не единственные способы сделать их передаваемыми, есть также encoding.BinaryMarshaler а также encoding.BinaryUnmarshaler которые проверены encoding/gob пакет.

В вашем случае вам нужно определить, какой "макет" памяти вы ожидаете, декодирование сбрасывает в значение вашего интерфейса. Когда вы объявляете

var v Incer

Вы не даете подсказке декодеру о том, как должно быть выполнено декодирование. если вы говорите декодеру, что ожидаете декодировать структуру main.A, она может использовать методы (*A).GobEncode() и (*A).GobDecode(), которые вы определили:

var v Incer
v = &A{}

Ссылка на игровую площадку: https://play.golang.org/p/WUNRKGTVIS

Это пример, приведенный в документации gob

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