在go中,值接收者操作的是结构体的副本,修改不会影响原实例;指针接收者则直接操作原实例,支持状态变更。选择不当会导致方法看似执行却无效果(如缓存未更新、字段未修改),这是初学者常见陷阱。
在Go语言中,方法接收者类型(func (s T) Method() vs func (s *T) Method())不仅关乎性能,更决定方法能否真正改变接收者的状态。以你的 SecureReader 为例:
type SecureReader struct {
pipe io.Reader
shared *[32]byte
decrypted bytes.Buffer // 可变状态:需写入解密数据
}
当你使用值接收者定义 decryptPipeIntoBuffer:
func (s SecureReader) decryptPipeIntoBuffer() (int, error) {
n, err := io.Copy(&s.decrypted, s.pipe) // ✅ 编译通过,但...
s.pipe = nil // ❌ 修改的是副本的 pipe 字段
return n, err
}
此时 s 是调用时 SecureReader 实例的一份完整拷贝(包括 decrypted 字段的副本)。io.Copy(&s.decrypted, …) 实际写入的是副本中的 bytes.Buffer,而原始 s.decrypted 完全未被触碰。方法返回后,副本被销毁,所有变更(如缓冲区内容、字段赋值)全部丢失——这正是你反复遇到 io.EOF 的根本原因:decrypted.Read(b) 始终读取一个空的、未填充的原始缓冲区。
而改为指针接收者后: