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
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.