英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论