gob在解码接口时发生了恐慌。

huangapple go评论70阅读模式
英文:

gob panics decoding an interface

问题

我有一个包含未导出字段的结构体,需要进行gob编码和解码。

例如:

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

显然,在这种情况下,我需要实现gob.GobEncodergob.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接口注释掉,一切又变得正常了。

我是否遗漏了什么重要的东西?因为描述的行为对我来说似乎相当奇怪。

英文:

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

答案1

得分: 4

问题在于Incer接口实现了一个特殊的接口,这个接口是encoding/gob包特有的:gob.GobDecoder

如果你的Incer接口只包含Inc()方法,那么它是可以工作的,因为gob解码器会看到你正在解码到一个接口类型,并且它会使用传输的类型来解码值,并在运行时检查解码的值(其类型包含在流中并传输)是否实现了目标接口类型,在这种情况下,解码成功。

如果Incer接口还嵌入了gob.GobEncodergob.GobDecoder接口,它们将负责进行编码/解码。如果一个类型实现了这些接口,解码器将不会尝试使用传输的类型来解码值,而是调用目标值的GobDecode()方法,如果需要的话创建一个零值。

由于你将nil值传递给了Decoder.Decode(),解码器需要创建一个零值,但它不知道要实例化的类型,因为你传递的值是一个指向接口的指针。你不能创建一个接口类型的值,只能创建一个满足某些接口的具体类型的值。

你不能在Incer接口中包含gob.GobEncodergob.GobDecoder。我知道你想确保实现它们,但正如你所看到的,你将无法将它们解码为"通用"的Incer接口值。而且我甚至不认为有必要在Incer中包含它们:gob.GobEncodergob.GobDecoder并不是使它们可传输的唯一方式,encoding/gob包还检查了encoding.BinaryMarshalerencoding.BinaryUnmarshaler

英文:

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.

答案2

得分: 1

在你的情况下,你需要定义内存的“布局”,以便将解码的转储数据放入你的接口值中。当你声明:

var v Incer

你没有给解码器任何关于如何进行解码的线索。如果你告诉解码器你期望解码一个 main.A 结构体,它可以使用你定义的方法 *(A).GobEncode()(*A).GobDecode()

var v Incer
v = &A{}

Playground 链接:https://play.golang.org/p/WUNRKGTVIS

这是 gob 文档 中提供的 示例

英文:

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

huangapple
  • 本文由 发表于 2017年4月10日 21:44:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/43324919.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定