Go的值接收者和指针接收者

Go 中没有类(class),类似的特性是结构(struct)。正如类可以有方法那样,结构也可以有方法。结构的方法有两种形式:值接收者(value receiver)和指针接收者(pointer receiver)。本文将讲一下它们的区别,以及我的个人理解。

区别一:能否修改原实例

值接收者拿到的是实例的副本,在方法内部修改这个副本,不会影响原实例。指针接收者拿到的是实例的地址,因此可以修改原实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

type Counter struct {
n int
}

func (c Counter) AddByValue() {
c.n++
}

func (c *Counter) AddByPointer() {
c.n++
}

func main() {
c := Counter{n: 0}

c.AddByValue()
fmt.Println(c.n) // 0

c.AddByPointer()
fmt.Println(c.n) // 1
}

区别二:调用规则

当运行 x.M() 时,Go 会先在 x 的方法集中寻找 M。在 Go 中,方法集的规则是:类型的方法集只包含值接收者,类型的指针的方法集同时包含值接收者和指针接收者。

另外,还有一个语法糖:如果 x可寻址(addressable)的,并且 M&x 的方法集中,x.M() 会自动转换成 (&x).M()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

type T struct{}

func (t T) ValueMethod() {}

func (t *T) PointerMethod() {}

func main() {
T{}.ValueMethod()
// T{}.PointerMethod() // compile error

(&T{}).ValueMethod()
(&T{}).PointerMethod()

u := T{}
u.ValueMethod()
u.PointerMethod() // 等价于 (&u).PointerMethod()

p := &T{}
p.ValueMethod()
p.PointerMethod()
}

不管是 u 还是 p,都可以调用值接收者和指针接收者。u 可寻址,所以可以通过语法糖调用 PointerMethodp 是类型的指针,它的方法集本身就包含了两个方法。

当直接写 T{}&T{} 时,情况就会略有不同。T{} 是临时值,不可寻址,所以无法使用语法糖调用 PointerMethod&T{} 也是临时值,但它的方法集本身就包含了两个方法。

区别三:接口规则

Go 判断一个类型是否满足一个接口时,只看它的方法集,并没有调用时的可寻址的语法糖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type T struct{}

func (t T) ValueMethod() {}

func (t *T) PointerMethod() {}

type ValueInterface interface {
ValueMethod()
}

type PointerInterface interface {
PointerMethod()
}

func main() {
var _ ValueInterface = T{}
var _ ValueInterface = &T{}
var _ PointerInterface = &T{}
// var _ PointerInterface = T{} // compile error
}

个人理解

在 C++ 中,类方法可以被标注为常量(const),相当于保证“这个方法不会修改当前实例”。常量方法不仅不能直接修改普通成员,还不能返回成员的引用或指针(常量版本除外)——因为引用和指针可以允许外部调用者绕过常量方法间接修改实例。

Go 的指针接收者可以用类似的方式理解。指针接收者相当于申请“这个方法需要修改当前实例”。考虑到在 Go 中,值大概率是副本——不论是把结构作为入参传给方法,或赋值给另一个变量,都会产生副本。在副本上修改不会影响原实例,也无法满足指针接收者的申请。所以只有类型的指针的方法集才包含指针接收者方法。

参考资料


Go的值接收者和指针接收者
https://tomzhu.site/2026/07/10/Go的值接收者和指针接收者/
作者
Tom Zhu
发布于
2026年7月10日
许可协议