Hello All,
I am attempting to serialize what for me is a fairly complicated structure.
I have used gob
in the past but for fairly simple things. I’ve identified a number of sticking points where I’m not sure how to proceed.
- (non) exported fields
- pointers - nil and cycles
- interfaces and generics
My domain is fairly complicated so I created a toy example that maintains the important parts of the code. I am trying to add serialization to an existing library so while I likely could change the code I’d like to see how far I can go with this.
I ultimately want a more dense graph, but for learning purposes I’ve reduced it so that the friends can only have one friend but ultimately would be a map
package friends
import (
"bytes"
"encoding/gob"
"log"
"strconv"
)
type Events = []float32
type Friend interface {
ID() string
Events() Events
}
type BasicFriend struct {
point float32
}
func (n BasicFriend) ID() string {
return strconv.FormatFloat(float64(n.point), 'f', -1, 32)
}
func (n BasicFriend) Events() []float32 {
return []float32{float32(n.point)}
}
// what I want eventually
type socialNetwork[T Friend] struct {
user Friend
friends map[string]*socialNetwork[T]
}
type lonelyNetwork[T Friend] struct {
user Friend
friends *lonelyNetwork[T]
}
func interfaceEncode(enc *gob.Encoder, p Friend) {
err := enc.Encode(&p)
if err != nil {
log.Fatal("encode:", err)
}
}
func interfaceDecode(dec *gob.Decoder) Friend {
var p Friend
err := dec.Decode(&p)
if err != nil {
log.Fatal("decode:", err)
}
return p
}
func (p *lonelyNetwork[T]) MarshalBinary() (_ []byte, err error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
// enc.Encode(p.user)
interfaceEncode(enc, p.user)
if p.friends == nil {
return buf.Bytes(), nil
}
isCyclic := p.friends != nil && p.friends.friends == p
enc.Encode(isCyclic)
if isCyclic {
p.friends.friends = nil
err = enc.Encode(p.friends)
p.friends.friends = p
} else {
err = enc.Encode(p.friends)
}
return buf.Bytes(), err
}
func (p *lonelyNetwork[T]) UnmarshalBinary(data []byte) (err error) {
dec := gob.NewDecoder(bytes.NewReader(data))
if err = dec.Decode(&p.user); err != nil {
return
}
var isCyclic bool
if err = dec.Decode(&isCyclic); err != nil {
return
}
// err = dec.Decode(&p.friends)
interfaceDecode(dec)
if isCyclic {
p.friends.friends = p
}
return
}
// These are required to encode BasicFriend as it exports no fields
func (d *BasicFriend) GobEncode() ([]byte, error) {
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
if err := encoder.Encode(d.point); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (d *BasicFriend) GobDecode(b []byte) error {
buf := bytes.NewBuffer(b)
decoder := gob.NewDecoder(buf)
if err := decoder.Decode(&d.point); err != nil {
return err
}
return nil
}
I’m currently stuck at the encoding of the Friend
interface. The code in it’s current from returns the following error
encode:gob: unaddressable value of type *hnsw.BasicFriend
If I do not pass to the interfaceEncode/Decode
then I’ll get
gob: local interface type *hnsw.Friend can only be decoded from remote interface type; received concrete type bool
I’m not sure if it’s picking up the isCyclic
flag or something else going on.