英文:
gob panics decoding an interface
问题
我有一个包含未导出字段的结构体,需要进行gob编码和解码。
例如:
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接口注释掉,一切又变得正常了。
我是否遗漏了什么重要的东西?因为描述的行为对我来说似乎相当奇怪。
英文:
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.GobEncoder
和gob.GobDecoder
接口,它们将负责进行编码/解码。如果一个类型实现了这些接口,解码器将不会尝试使用传输的类型来解码值,而是调用目标值的GobDecode()
方法,如果需要的话创建一个零值。
由于你将nil
值传递给了Decoder.Decode()
,解码器需要创建一个零值,但它不知道要实例化的类型,因为你传递的值是一个指向接口的指针。你不能创建一个接口类型的值,只能创建一个满足某些接口的具体类型的值。
你不能在Incer
接口中包含gob.GobEncoder
和gob.GobDecoder
。我知道你想确保实现它们,但正如你所看到的,你将无法将它们解码为"通用"的Incer
接口值。而且我甚至不认为有必要在Incer
中包含它们:gob.GobEncoder
和gob.GobDecoder
并不是使它们可传输的唯一方式,encoding/gob
包还检查了encoding.BinaryMarshaler
和encoding.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
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论