Go:方法调用后对象不持久化

huangapple go评论130阅读模式
英文:

Go: Object not persistent after method call

问题

我正在尝试为我正在编写的程序实现MarshalBinary和UnmarshalBinary,但是在调用UnmarshalBinary之后,我的更改似乎没有持久保存。

我的最小工作示例(MWE)如下:

  1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "strconv"
  6. )
  7. type test struct {
  8. var1 uint32
  9. var2 uint32
  10. }
  11. func (self test) MarshalBinary() ([]byte, error) {
  12. tmp := make([]byte, 8)
  13. binary.BigEndian.PutUint32(tmp[0:4], self.var1)
  14. binary.BigEndian.PutUint32(tmp[4:8], self.var2)
  15. return tmp, nil
  16. }
  17. func (self test) UnmarshalBinary(data []byte) error {
  18. self.var1 = binary.BigEndian.Uint32(data[0:4])
  19. self.var2 = binary.BigEndian.Uint32(data[4:8])
  20. fmt.Printf("UMB\t%s\n", self.String())
  21. return nil
  22. }
  23. func (self test) String() string {
  24. return "test struct\tvar1 = 0x" +
  25. strconv.FormatUint(uint64(self.var1), 16) +
  26. "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
  27. }
  28. func main() {
  29. in := test{
  30. var1: uint32(0x39471208),
  31. var2: uint32(0x45387182),
  32. }
  33. fmt.Printf("In\t%s\n", in.String())
  34. bin, _ := in.MarshalBinary()
  35. fmt.Printf("Bin\t0x%x\n", bin)
  36. var out test
  37. out.UnmarshalBinary(bin)
  38. fmt.Printf("Out\t%s\n", out.String())
  39. }

我的输出是:

  1. In test struct var1 = 0x39471208 var2 = 45387182
  2. Bin 0x3947120845387182
  3. UMB test struct var1 = 0x39471208 var2 = 45387182
  4. Out test struct var1 = 0x0 var2 = 0
英文:

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:

  1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "strconv"
  6. )
  7. type test struct {
  8. var1 uint32
  9. var2 uint32
  10. }
  11. func (self test) MarshalBinary() ([]byte, error) {
  12. tmp := make([]byte, 8)
  13. binary.BigEndian.PutUint32(tmp[0:4], self.var1)
  14. binary.BigEndian.PutUint32(tmp[4:8], self.var2)
  15. return tmp, nil
  16. }
  17. func (self test) UnmarshalBinary(data []byte) error {
  18. self.var1 = binary.BigEndian.Uint32(data[0:4])
  19. self.var2 = binary.BigEndian.Uint32(data[4:8])
  20. fmt.Printf("UMB\t%s\n", self.String())
  21. return nil
  22. }
  23. func (self test) String() string {
  24. return "test struct\tvar1 = 0x" +
  25. strconv.FormatUint(uint64(self.var1), 16) +
  26. "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
  27. }
  28. func main() {
  29. in := test{
  30. var1: uint32(0x39471208),
  31. var2: uint32(0x45387182),
  32. }
  33. fmt.Printf("In\t%s\n", in.String())
  34. bin, _ := in.MarshalBinary()
  35. fmt.Printf("Bin\t0x%x\n", bin)
  36. var out test
  37. out.UnmarshalBinary(bin)
  38. fmt.Printf("Out\t%s\n", out.String())
  39. }

And my output:

  1. In test struct var1 = 0x39471208 var2 = 45387182
  2. Bin 0x3947120845387182
  3. UMB test struct var1 = 0x39471208 var2 = 45387182
  4. Out test struct var1 = 0x0 var2 = 0

答案1

得分: 2

self参数不是引用类型,所以当你调用test.UnmarshalBinary()方法时,它是按值进行复制的,整个结构体被复制到堆栈上,并在UnmarshalBinary()返回时释放。来自《Go之旅》第54页的幻灯片:

使用指针接收器有两个原因。首先,避免在每次方法调用时复制值(如果值类型是一个大的结构体,则更高效)。...

(我很难找到一个官方来源说接收器是按值传递的;有人知道更权威的来源吗?)

尝试将接收器更改为接受指针接收器:

  1. func (self *test) MarshalBinary() ([]byte, error) {
  2. tmp := make([]byte, 8)
  3. binary.BigEndian.PutUint32(tmp[0:4], self.var1)
  4. binary.BigEndian.PutUint32(tmp[4:8], self.var2)
  5. return tmp, nil
  6. }
  7. func (self *test) UnmarshalBinary(data []byte) error {
  8. self.var1 = binary.BigEndian.Uint32(data[0:4])
  9. self.var2 = binary.BigEndian.Uint32(data[4:8])
  10. fmt.Printf("UMB\t%s\n", self.String())
  11. return nil
  12. }
  13. func (self *test) String() string {
  14. return "test struct\tvar1 = 0x" +
  15. strconv.FormatUint(uint64(self.var1), 16) +
  16. "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
  17. }

然后你将看到更好的输出:

  1. In test struct var1 = 0x39471208 var2 = 45387182
  2. Bin 0x3947120845387182
  3. UMB test struct var1 = 0x39471208 var2 = 45387182
  4. Out test struct var1 = 0x39471208 var2 = 45387182
英文:

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:

  1. func (self *test) MarshalBinary() ([]byte, error) {
  2. tmp := make([]byte, 8)
  3. binary.BigEndian.PutUint32(tmp[0:4], self.var1)
  4. binary.BigEndian.PutUint32(tmp[4:8], self.var2)
  5. return tmp, nil
  6. }
  7. func (self *test) UnmarshalBinary(data []byte) error {
  8. self.var1 = binary.BigEndian.Uint32(data[0:4])
  9. self.var2 = binary.BigEndian.Uint32(data[4:8])
  10. fmt.Printf("UMB\t%s\n", self.String())
  11. return nil
  12. }
  13. func (self *test) String() string {
  14. return "test struct\tvar1 = 0x" +
  15. strconv.FormatUint(uint64(self.var1), 16) +
  16. "\tvar2 = " + strconv.FormatUint(uint64(self.var2), 16)
  17. }

And you will see better output:

  1. In test struct var1 = 0x39471208 var2 = 45387182
  2. Bin 0x3947120845387182
  3. UMB test struct var1 = 0x39471208 var2 = 45387182
  4. Out test struct var1 = 0x39471208 var2 = 45387182

huangapple
  • 本文由 发表于 2014年5月5日 01:29:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/23459277.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定