指针接收者能修改原值,值接收者只修改副本;接口实现要求严格匹配接收者类型;小结构体(

指针接收者能改原值,值接收者改的是副本
这是最直接、最常踩坑的区别。只要方法内部有赋值操作(比如 u.Name = "new" 或 h.age++),用值接收者就等于白干——原始变量完全不变。
- 值接收者:每次调用都复制整个结构体,方法里所有字段修改只发生在栈上临时副本,返回即销毁
- 指针接收者:传的是地址,
p.Name = "new"直接写入原始内存位置 - 典型现象:
user.SetName("Alice")调用完user.Name还是旧值 → 说明SetName用了值接收者
接口实现不匹配:User 和 *User 是两种类型
Go 不会自动把 User 当成 *User 去满足接口,哪怕只差一个 *。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 如果接口方法定义在
*User上(如func (u *User) Save() error),那么只有&user能赋值给该接口变量 -
var s Saver = user会报错:User does not implement Saver (Save method has pointer receiver) - 反过来,如果所有方法都是值接收者,
*user也能满足接口(Go 自动解引用),但user不能调用指针接收者方法
小结构体用值接收者反而更高效
不是“一律用指针”,小于约 24 字节且不含指针字段的结构体(如 type Point struct{X, Y int}),值接收者更快。
- 原因:复制两个
int比传一个指针还轻量,没有 cache miss 风险 - 语义更清晰:
(p Point) Distance()表达“计算派生值”,天然不可变,调用方无副作用顾虑 - 反例:含
[]byte、map、大数组或嵌套指针的结构体,哪怕只有 2 个字段,也建议用指针接收者——切片头和 map header 虽小,背后数据可能巨大
自动取址是语法糖,不是万能解药
你写 c.Inc() 能成功,是因为 c 是可寻址变量,Go 编译器悄悄帮你转成 (&c).Inc()。但这不解决根本问题。
- 陷阱 1:
Counter{}.Inc()报错:cannot call pointer method on Counter literal—— 字面量不可取址 - 陷阱 2:
makeCounter().Inc()同样失败 —— 函数返回的临时值不可寻址 - 关键点:自动取址只让“能调”,不改变“改不改得动”的本质;只要接收者是
*T,它就在修改原始对象,这点从不妥协
SetName 用指针接收者改状态,GetName 却用值接收者——表面能跑,但接口实现可能断掉,调用方也搞不清哪些方法会改变自己持有的实例。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










