I'm storing encrypted gobs in a k-v database and was hoping to have a convenience function in a package which takes a key and an interface, it decrypts the stored value, decodes it into the passed interface and the consumer code can hum along without having to know anything about the data being stored.
I can't for the world figure out why I fail to write to the pointer value using gob, when it works with the json encoder. Dumbed-down the code looks like this:
type Foo struct {
A string
B string
}
func marshal(i interface{}) ([]byte, error) {
var indexBuffer bytes.Buffer
encoder := gob.NewEncoder(&indexBuffer)
err := encoder.Encode(&i)
return indexBuffer.Bytes(), err
}
func unmarshal(data []byte, e interface{}) error {
buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf)
err := dec.Decode(&e)
fmt.Println("Unmarshal", e)
return err
}
func marshalJ(i interface{}) ([]byte, error) {
return json.Marshal(i)
}
func unmarshalJ(data []byte, e interface{}) error {
return json.Unmarshal(data, e)
}
func main() {
foo := Foo{"Hello", "world!"}
gob.Register(Foo{})
data, err := marshal(foo)
fmt.Println("got", len(data), err)
var bar Foo
err = unmarshal(data, &bar)
fmt.Println("Main err", err)
fmt.Println("Main", bar)
fmt.Println("-------------------------")
data, err = marshalJ(foo)
fmt.Println("got J", len(data), err)
err = unmarshalJ(data, &bar)
fmt.Println("Main J err", err)
fmt.Println("Main J", bar)
}
The unmarshalJ behaves like I expect, that is in Main my bar variable is a copy of foo since unmarshalJ receives a pointer to bar and passes it on.
The unmarshal function on the other hand have the parameter changed locally, but the change isn't made to the bar in Main. Internally gob.Decode passes on reflect.ValueOf, and I have always tried to avoid using reflect so I don't really understand what that does.
My two questions would be:
- Is it possible to have a wrapper like I want and have the caller's value changed like it works with the json encoder?
- More of a bonus question because this makes me feel I'm not fully grasping pointers in go: How can the value of unmarshal.e be changed, but Main.bar not be changed when unmarshal.e is a pointer to Main.bar? Another thing which confuse me here is that inside unmarshal I have to pass &e to Decode or it fails with
want struct type main.Foo; got non-struct
.
CodePudding user response:
There are two issues, 1st is that you give unmarshal
a pointer, and then make another pointer inside unmarshal
itself, so you end up passing **Foo
to the gob decoder.
The second issue is that you take pointers of the interface{}
'es inside the function. This somehow effects how the data is encoded. Everything works if you pass in pointers to the functions and don't modify the variables inside the functions.
The fixed code looks like this, playground link:
type Foo struct {
A string
B string
}
func marshal(i interface{}) ([]byte, error) {
var indexBuffer bytes.Buffer
encoder := gob.NewEncoder(&indexBuffer)
err := encoder.Encode(i)
return indexBuffer.Bytes(), err
}
func unmarshal(data []byte, e interface{}) error {
buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf)
err := dec.Decode(e)
fmt.Println("Unmarshal", e)
return err
}
func marshalJ(i interface{}) ([]byte, error) {
return json.Marshal(i)
}
func unmarshalJ(data []byte, e interface{}) error {
return json.Unmarshal(data, e)
}
func main() {
foo := Foo{"Hello", "world!"}
gob.Register(Foo{})
data, err := marshal(&foo)
fmt.Println("got", len(data), err)
var bar Foo
err = unmarshal(data, &bar)
fmt.Println("Main err", err)
fmt.Println("Main", bar)
fmt.Println("-------------------------")
data, err = marshalJ(foo)
fmt.Println("got J", len(data), err)
err = unmarshalJ(data, &bar)
fmt.Println("Main J err", err)
fmt.Println("Main J", bar)
}