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

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

Go: Object not persistent after method call

问题

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

我的最小工作示例(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())
}

我的输出是:

In	test struct	var1 = 0x39471208	var2 = 45387182
Bin	0x3947120845387182
UMB	test struct	var1 = 0x39471208	var2 = 45387182
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:

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

答案1

得分: 2

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

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

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

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

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)
}

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

In    test struct    var1 = 0x39471208    var2 = 45387182
Bin   0x3947120845387182
UMB   test struct    var1 = 0x39471208    var2 = 45387182
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:

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

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:

确定