在结构体中解码接口和解码原始数据之间的区别是什么?

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

Difference between gob decoding an interface in a struct, vs. raw

问题

我一直在努力理解在结构体中嵌入接口类型时,对接口类型进行编码/解码与不嵌入时的区别。

使用以下示例:在playground中查看

请注意,代码声明了一个接口IFace。它声明了一个非导出的结构体impl。它设置了一些Gob方法来RegisterGobEncodeGobDecode impl结构体。

然后,它还声明了一个导出的结构体Data,它有一个名为Foo的字段,类型为接口类型IFace

所以,这里有一个接口,一个实现它的结构体,以及一个包含一个值为该接口类型的字段的容器结构体。

我的问题是,容器结构体Data可以顺利通过Gob进行传输,并且在传输过程中可以顺利对Data结构体中的IFace字段进行编码和解码...太好了!但是,我似乎无法仅仅通过gob传输IFace值的实例。

我错过了什么魔法调用?

搜索错误消息会得到很多结果,但我相信我已经满足了Gob的要求...而且这在成功的结构体编码中得到了"证明"。显然我错过了什么,但是看不到。

请注意,程序的输出是:

Encoding {IFace:bilbo} now
Encoding IFace:baggins now
Decoded {IFace:bilbo} now
decode error: gob: local interface type *main.IFace can only be decoded from remote interface type; received concrete type impl
Decoded <nil> now

实际的代码如下:

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type IFace interface {
    FooBar() string
}

type impl struct {
    value string
}

func init() {
    gob.Register(impl{})
}

func (i impl) FooBar() string {
    return i.value
}

func (i impl) String() string {
    return "IFace:" + i.value
}

func (i impl) GobEncode() ([]byte, error) {
    return []byte(i.value), nil
}

func (i *impl) GobDecode(dat []byte) error {
    val := string(dat)
    i.value = val
    return nil
}

func newIFace(val string) IFace {
    return impl{val}
}

type Data struct {
    Foo IFace
}

func main() {

    var network bytes.Buffer        // Stand-in for a network connection
    enc := gob.NewEncoder(&network) // Will write to network.
    dec := gob.NewDecoder(&network) // Will read from network.

    var err error

    var bilbo IFace
    bilbo = newIFace("bilbo")

    var baggins IFace
    baggins = newIFace("baggins")

    dat := Data{bilbo}

    fmt.Printf("Encoding %v now\n", dat)
    err = enc.Encode(dat)
    if err != nil {
        fmt.Println("encode error:", err)
    }

    fmt.Printf("Encoding %v now\n", baggins)
    err = enc.Encode(baggins)
    if err != nil {
        fmt.Println("encode error:", err)
    }

    var pdat Data
    err = dec.Decode(&pdat)
    if err != nil {
        fmt.Println("decode error:", err)
    }
    fmt.Printf("Decoded %v now\n", pdat)

    var pbag IFace
    err = dec.Decode(&pbag)
    if err != nil {
        fmt.Println("decode error:", err)
    }
    fmt.Printf("Decoded %v now\n", pbag)

}
英文:

I have been struggling to understand the difference between encoding/decoding an interface type when that type is embedded in a struct, vs. when it's not embedded at all.

Using the following example: here in the playground

Note the code declares an interface IFace. It declares a non-exported struct impl. It sets up a few Gob methods to Register, GobEncode, and GobDecode the impl struct.

Then, it also declares a struct Data which is exported, and has a single field Foo which is of the interface type IFace.

So, there's an interface, a struct which implements it, and a container struct which has a field whose value is that interface type.

My problem is that the container struct Data is happily sent through the Gob gauntlet, and, when it goes through, it happily encodes and decodes the IFace field in the Data struct... great! But, I cannot seem to be able to send just an instance of the IFace value through the gob gauntlet.

What is the magic invocation I am missing?

Searching for the error message gives a number of results, but I believe I have satisfied the Gob contract.... and the "proof" of that is in the successful struct gobbing. Obviously I have missed something, but can't see it.

Note, the output of the program is:

Encoding {IFace:bilbo} now
Encoding IFace:baggins now
Decoded {IFace:bilbo} now
decode error: gob: local interface type *main.IFace can only be decoded from remote interface type; received concrete type impl
Decoded &lt;nil&gt; now

The actual code is:

package main

import (
	&quot;bytes&quot;
	&quot;encoding/gob&quot;
	&quot;fmt&quot;
)

type IFace interface {
	FooBar() string
}

type impl struct {
	value string
}

func init() {
	gob.Register(impl{})
}

func (i impl) FooBar() string {
	return i.value
}

func (i impl) String() string {
	return &quot;IFace:&quot; + i.value
}

func (i impl) GobEncode() ([]byte, error) {
	return []byte(i.value), nil
}

func (i *impl) GobDecode(dat []byte) error {
	val := string(dat)
	i.value = val
	return nil
}

func newIFace(val string) IFace {
	return impl{val}
}

type Data struct {
	Foo IFace
}

func main() {

	var network bytes.Buffer        // Stand-in for a network connection
	enc := gob.NewEncoder(&amp;network) // Will write to network.
	dec := gob.NewDecoder(&amp;network) // Will read from network.

	var err error

	var bilbo IFace
	bilbo = newIFace(&quot;bilbo&quot;)

	var baggins IFace
	baggins = newIFace(&quot;baggins&quot;)

	dat := Data{bilbo}

	fmt.Printf(&quot;Encoding %v now\n&quot;, dat)
	err = enc.Encode(dat)
	if err != nil {
		fmt.Println(&quot;encode error:&quot;, err)
	}

	fmt.Printf(&quot;Encoding %v now\n&quot;, baggins)
	err = enc.Encode(baggins)
	if err != nil {
		fmt.Println(&quot;encode error:&quot;, err)
	}

	var pdat Data
	err = dec.Decode(&amp;pdat)
	if err != nil {
		fmt.Println(&quot;decode error:&quot;, err)
	}
	fmt.Printf(&quot;Decoded %v now\n&quot;, pdat)

	var pbag IFace
	err = dec.Decode(&amp;pbag)
	if err != nil {
		fmt.Println(&quot;decode error:&quot;, err)
	}
	fmt.Printf(&quot;Decoded %v now\n&quot;, pbag)

}

答案1

得分: 6

调用

err = enc.Encode(baggins)

将一个impl值传递给Encode。它没有传递IFace类型的值。文档http://research.swtch.com/interfaces可能有助于理解为什么这样做。该值被编码为具体类型impl

如果你想解码为接口类型,那么你必须编码一个接口类型。一种方法是传递指向接口值的指针:

err = enc.Encode(&baggins)

在这个调用中,传递了一个*IFace给Encode。在解引用指针之后,编码器会看到该值是一个接口类型,并将其编码为接口类型。因为gob包在转换值时会进行所有必要的解引用和间接引用,所以在调用Encode时的额外间接引用级别不需要在解码时再进行额外的间接引用。

playground示例

英文:

The call

err = enc.Encode(baggins)

passes an impl value to Encode. It does not pass a value of type IFace. The document http://research.swtch.com/interfaces may be helpful in understanding why this is. The value is encoded as the concrete type impl.

If you want to decode to an interface type, then you must encode an interface type. One way to do this is to pass a pointer to the interface value:

err = enc.Encode(&amp;baggins)

In this call, a *IFace is passed to Encode. After dereferencing the pointer, the encoder sees that the value is an interface type and encodes it as an interface type. Because the gob package does all necessary dereferencing and indirection when converting values, the extra level of indirection on the call to Encode does not require an extra level of indirection when decoding.

<kbd>playground example</kbd>

huangapple
  • 本文由 发表于 2016年1月26日 10:35:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/35006167.html
匿名

发表评论

匿名网友

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

确定