指针接收器和值接收器之间的区别是什么?

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

What is the difference between Pointer and Value receiver types?

问题

我正在写一个程序,它从io.Reader中读取数据并将其缓存在bytes.Buffer中。

type SecureReader struct {
    pipe      io.Reader
    shared    *[32]byte
    decrypted bytes.Buffer
}

func (s SecureReader) Read(b []byte) (int, error) {
    s.decryptPipeIntoBuffer()
    return s.decrypted.Read(b)
}

func (s SecureReader) decryptPipeIntoBuffer() (int, error) {/*很多代码...*/}

起初,我使用了值接收器,因为我以为它们是一样的。然而,我注意到当调用SecureReader.Read()方法时,它什么也不做,总是返回io.EOF。

我苦思冥想并将接收器类型更改为:

func (s *SecureReader) decryptPipeIntoBuffer() (int, error) {/*很多代码...*/}

现在我的代码神奇地正常工作了。发生了什么?

英文:

I was writing a program that reads data from a io.Reader and caches them in a bytes.Buffer.

type SecureReader struct {
	pipe      io.Reader
	shared    *[32]byte
	decrypted bytes.Buffer
}

func (s SecureReader) Read(b []byte) (int, error) {
	s.decryptPipeIntoBuffer()
	return s.decrypted.Read(b)
}

func (s SecureReader) decryptPipeIntoBuffer() (int, error) {/*Lots of code...*/}

I first used a value receiver, because I thought they were the same. However, I noticed my method does not do anything when invoked: SecureReader.Read() would always return io.EOF.

I banged my head around and changed the receiver type to

func (s *SecureReader) decryptPipeIntoBuffer() (int, error) {/*Lots of code...*/}

Now my code magically works. What is going on?

答案1

得分: 3

一个值接收器在 SecureReader 实例 s副本 上操作。

如果该方法修改了实例的 副本 的任何部分(比如修改 s.decrypted),一旦方法退出,这些修改对于原始实例是不可见的。

而使用指针接收器则不同,该方法在实际的 SecureReader 实例 s 上操作并可以修改它,因为指针的 副本 被传递给方法。


在“不要被Go中的指针和非指针方法接收器所困扰”中可以看到更多示例。

简单来说:你可以将接收器视为传递给方法的参数。所有你可能希望按值传递或按引用传递的原因都适用。

为什么要选择按引用传递而不是按值传递的原因:

  • 你想要实际修改接收器(“读/写”而不仅仅是“读取”)
  • 结构体非常大,深拷贝代价昂贵
  • 一致性:如果结构体的某些方法具有指针接收器,其余方法也应该有。这样可以保证行为的可预测性。

如果你需要这些特性来调用方法,请使用指针接收器。

英文:

A value receiver operates on a copy of the SecureReader instance s.

If the method mutates any part of the copy of the instance (like modify s.decrypted), it is not visible on the original instance of the receiver, once the method exit.

That changes with a pointer receiver, where the method operates and can mutates the actual SecureReader instance s, since a copy of the pointer is passed to the method.


See more examples in "Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang".

> Simply stated: you can treat the receiver as if it was an argument being passed to the method. All the same reasons why you might want to pass by value or pass by reference apply.

> Reasons why you would want to pass by reference as opposed to by value:

> - You want to actually modify the receiver (“read/write” as opposed to just “read”)

  • The struct is very large and a deep copy is expensive
  • Consistency: if some of the methods on the struct have pointer receivers, the rest should too. This allows predictability of behavior

> If you need these characteristics on your method call, use a pointer receiver.

huangapple
  • 本文由 发表于 2015年4月6日 03:17:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/29461227.html
匿名

发表评论

匿名网友

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

确定