3

I am attempting to implement an interface based message queue where jobs are pushed as bytes to a redis queue. But I keep receiving an EOF error when attempting to decode the byte stream.

https://play.golang.org/p/l9TBvcn9qg

Could someone point me in the right direction?

Thank you!

alienchow
  • 383
  • 2
  • 7
  • 2
    The enc.Encode(test) function returns an error, it's good practice to capture and check all possible errors – Mark Oct 12 '15 at 05:22
  • Thanks for the reminder. I do usually catch and handle errors, but in this case I'm just trying to demonstrate a problem in encoding and decoding nested interfaces. – alienchow Oct 12 '15 at 05:53
  • 2
    Sorry, I mean that call is returning an error, and when running the code and capturing the error, it shows "gob: type not registered for interface: main.B". Adding a gob.Register(B{}) before encoding prevents the EOF. – Mark Oct 12 '15 at 06:32
  • I TOTALLY forgot about having to register! Thank you Mark! I'll take note to check for the error." – alienchow Oct 12 '15 at 06:44
  • Questions and answers should include all the relevant code in them an not rely on a third party site (Go playground links are nice, but they should not be the only way to understand the question). – Dave C Oct 12 '15 at 16:42

3 Answers3

0

In your Go Playground example, you're trying to encode an interface and interfaces don't have a concrete implementation. If you remove the interface from your A struct, that should work. Like the following:

package main

import "fmt"
import "encoding/gob"
import "bytes"

type testInterface interface{}

type A struct {
  Name string
  Interface *B // note this change here
}

type B struct {
  Value string
}

func main() {
  var err error
  test := &A {
    Name: "wut",
    Interface: &B{Value: "BVALUE"},
  }
  buf := bytes.NewBuffer([]byte{})
  enc := gob.NewEncoder(buf)
  dec := gob.NewDecoder(buf)

  // added error checking as per Mark's comment
  err = enc.Encode(test)
  if err != nil {
    panic(err.Error())
  }

  result := &A{}
  err := dec.Decode(result)
  fmt.Printf("%+v\n", result)
  fmt.Println("Error is:", err)
  fmt.Println("Hello, playground")
}

Also, just as a side note you will see some sort of output like the following: &{Name:wut Interface:0x1040a5a0} because A is referencing a reference to a B struct. To clean that up further:

type A struct{
  Name string
  Interface B // no longer a pointer
}

func main() {
   // ...
   test := &A{Name: "wut", Interface: B{Value: "BVALUE"}}
   // ...
}
Will C
  • 1,750
  • 10
  • 20
  • I'm using interfaces to abstract the job queue so that I don't have to switch case assert to a specific struct type everytime. (There are many structs other than B implementing the testInterface) Does this mean that I have no way to abstract a child field into an interface? – alienchow Oct 12 '15 at 05:51
  • Great! Thats nice to know. I haven't personally used the `gob` package myself. I've used other `encoding` packages so I assumed it was the same behavior. – Will C Oct 12 '15 at 13:04
0

Found the answer to the problem from Mark above. I have forgotten to do a gob.Register(B{})

https://play.golang.org/p/7rQDHvMhD7

alienchow
  • 383
  • 2
  • 7
0

I got this error by trying to load gob files which did not save() successfully, but I lacked sufficient error handling on save() to know it.

My save function is now a little insane, but I like it:

func SaveToDisk[T any](filepath string, data T) {
    f, err := os.Create(filepath)
    if err != nil {
        log.Fatal(errors.Wrap(err, "os.Create()"))
    }
    defer func() {
        err := f.Close()
        if err != nil {
            log.Fatal(errors.Wrap(err, "f.Close()"))
        }
    }()

    writer := bufio.NewWriter(f)
    dataEncoder := gob.NewEncoder(writer)
    err = dataEncoder.Encode(data)
    if err != nil {
        log.Fatal(errors.Wrap(err, "dataEncoder.Encode()"))
    }
    err = writer.Flush() // Flush the data to the file
    if err != nil {
        log.Fatal(errors.Wrap(err, "writer.Flush()"))
    }
}

Turns out I was missing the error dataEncoder.Encode(): gob: type not registered for interface: map[string]interface {} during save().

gob cannot seem to marshal interfaces without any help, but you can register interface types with it to remedy that. To fix my problem, all I had to do was:

func init() {
    gob.Register(map[string]interface{}{})
}
Astockwell
  • 1,498
  • 13
  • 11