6

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 <nil> now

The actual code is:

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)

}
4

1 回答 1

7

通话

err = enc.Encode(baggins)

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

如果要解码为接口类型,则必须对接口类型进行编码。一种方法是传递一个指向接口值的指针:

err = enc.Encode(&baggins)

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

playground example

于 2016-01-26T04:54:01.993 回答