ILikeFood
ILikeFood

Reputation: 560

Go: Object not persistent after method call

I am trying to implement MarshalBinary and UnmarshalBinary for a program I am writing, but my changes do not seem to persist after UnmarshalBinary is called.

My MWE:

package main

import (
    "encoding/binary"
    "fmt"
    "strconv"
)

type test struct {
    var1 uint32
    var2 uint32
}

func (self test) MarshalBinary() ([]byte, error) {
    tmp := make([]byte, 8)
    binary.BigEndian.PutUint32(tmp[0:4], self.var1)
    binary.BigEndian.PutUint32(tmp[4:8], self.var2)
    return tmp, nil
}

func (self test) UnmarshalBinary(data []byte) error {
    self.var1 = binary.BigEndian.Uint32(data[0:4])
    self.var2 = binary.BigEndian.Uint32(data[4:8])
    fmt.Printf("UMB\t%s\n", self.String())
    return nil
}

func (self test) String() string {
    return "test struct\tvar1 = 0x" +
        strconv.FormatUint(uint64(self.var1), 16) +
        "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
}

func main() {
    in := test{
        var1: uint32(0x39471208),
        var2: uint32(0x45387182),
    }
    fmt.Printf("In\t%s\n", in.String())
    bin, _ := in.MarshalBinary()
    fmt.Printf("Bin\t0x%x\n", bin)
    var out test
    out.UnmarshalBinary(bin)
    fmt.Printf("Out\t%s\n", out.String())
}

And my output:

In  test struct var1 = 0x39471208   var2 = 45387182
Bin 0x3947120845387182
UMB test struct var1 = 0x39471208   var2 = 45387182
Out test struct var1 = 0x0  var2 = 0

Upvotes: 0

Views: 108

Answers (1)

ILikeFood
ILikeFood

Reputation: 560

The self parameter is not a reference type so it is copied by value when you call the method test.UnmarshalBinary(), the entire struct is copied onto the stack, and freed when UnmarshalBinary() returns. From slide 54 of A Tour of Go:

There are two reasons to use a pointer receiver. First, to avoid copying the value on each method call (more efficient if the value type is a large struct). ...

(It was kind of hard for me to find an official source saying the receiver is passed by value; does anyone know of a more authoritative one?)

Try changing your receivers to accept pointer receivers:

func (self *test) MarshalBinary() ([]byte, error) {
    tmp := make([]byte, 8)
    binary.BigEndian.PutUint32(tmp[0:4], self.var1)
    binary.BigEndian.PutUint32(tmp[4:8], self.var2)
    return tmp, nil
}

func (self *test) UnmarshalBinary(data []byte) error {
    self.var1 = binary.BigEndian.Uint32(data[0:4])
    self.var2 = binary.BigEndian.Uint32(data[4:8])
    fmt.Printf("UMB\t%s\n", self.String())
    return nil
}

func (self *test) String() string {
    return "test struct\tvar1 = 0x" +
        strconv.FormatUint(uint64(self.var1), 16) +
        "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
}

And you will see better output:

In  test struct var1 = 0x39471208   var2 = 45387182
Bin 0x3947120845387182
UMB test struct var1 = 0x39471208   var2 = 45387182
Out test struct var1 = 0x39471208   var2 = 45387182

Upvotes: 2

Related Questions