gob panics decoding an interface
Asked Answered
M

2

6

I have a struct with unexported fields which should be gob encoded and decoded.

Say:

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

Obviously in that case I need to implement gob.GobEncoder and gob.GobDecoder interfaces. And if I use the struct directly everything works fine:

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

But I also need to have an interface that implements same logic and is serializable:

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

Full code: https://play.golang.org/p/Zig2mPrnrq

And suddenly it panics:

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

But if I comment gob interfaces out everything become fine again.

Am I missing something important? Cos the described behavior seems quite strange to me

Mickey answered 10/4, 2017 at 13:44 Comment(0)
L
6

The problem lies in the fact that the Incer interface implements a special interface, special to the encoding/gob package: gob.GobDecoder.

If your Incer interface only contains the Inc() method, it will work because the gob decoder sees you're decoding into an interface type, and it will use the transmitted type to decode the value and check at runtime if the decoded value (whose type is included and transmitted in the stream) implements the destination interface type, and in this case it will:

type Incer interface {
    Inc()
}

So decoding succeeds.

If the Incer interface also embeds the gob.GobEncoder and gob.GobDecoder interfaces, they are –by definition– responsible to do the coding / decoding. If a type implements these interfaces, the decoder will not attempt to decode the value using the transmitted type, but will instead call GobDecode() method of the destination value, creating a zero value if needed.

Since you pass a nil value to Decoder.Decode(), the decoder needs to create a zero-value, but it doesn't know what type to instantiate, because the value you pass is a pointer to interface. You cannot create a value of interface type, only a value of a concrete type that may satisfy certain interfaces.

You can't include gob.GobEncoder and gob.GobDecoder in your Incer interface. I know you want to make sure the implementations do implement them, but then –as you can see– you won't be able to decode them into a "general" Incer interface value. Also I don't even see the need to include them in Incer: gob.GobEncoder and gob.GobDecoder are not the only ways to make them transmittable, there are also encoding.BinaryMarshaler and encoding.BinaryUnmarshaler that are checked by the encoding/gob package.

Lugsail answered 10/4, 2017 at 14:42 Comment(1)
Note that you will receive a similar panic if the encoding.BinaryUnmarshaler interface is part of the interface (panic: interface conversion: interface is nil, not encoding.BinaryUnmarshaler).Giguere
L
1

In your case, you need to define what is the "layout" of memory you're expecting the decode dumps into your interface value. When you declare

var v Incer

You're not giving any clue to the decoder about how the decoding must be done. if you say to the decoder you're expecting to decode an main.A struct, it can use the methods (*A).GobEncode() and (*A).GobDecode() you defined:

var v Incer
v = &A{}

Playground link: https://play.golang.org/p/WUNRKGTVIS

This is the example provided in the gob documentation

Loom answered 10/4, 2017 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.