go 中关于 T 和 *T 方法集的一点疑问

127 天前
 aababc

今天看了一个说法,*T 类型会在编译期间生成所有 T 类型的同名包装方法, 大家有理解这句话是啥意思的吗? 如果生成同名包装方法,针对 T 类型中有副作用的方法(不会真的影响原值),会在 *T 中真的对原值产生影响吗。

1442 次点击
所在节点    Go 编程语言
6 条回复
PTLin
127 天前
就想象成解引用然后运行,没什么太特殊的。
我以前这块记过点笔记看看能不能帮上你 https://imgur.com/a/EUVihfK
yicixin
108 天前
是不是说反了,应该是 T 类型接收器的方法会在编译期间生成对应的*T 类型接收器的同名包装方法。

会不会在对应的*T 的包装方法中对原值产生影响,简单写一个测试就能知道,并不会影响,我猜测*T 包装方法内部可能是解引用再调用 T 的方法。

证明:
使用的代码:
package main

type entity struct {
data [2048]byte
v int
}

func (e entity) inc() {
e.v++
}

type incer interface {
inc()
}

func doSomething(i incer) {
i.inc()
}

func main() {
var e = entity{}
doSomething(&e)
}

1. 在 e.v++处设置断点,debug 运行可以断住,说明最终还是调用了 T 的方法
2. 断点时查看函数调用栈,对比 func (e entity) inc()和 func (e *entity) inc()


使用的代码:
package main

type entity struct {
data [2048]byte
v int
}

func (e entity) inc() {
e.v++
}

type incer interface {
inc()
}

func doSomething(i incer) {
i.inc()
}

func main() {
var e = entity{}
doSomething(&e)
}

1. 在 e.v++处设置断点,debug 运行可以断住,说明最终还是调用了 T 的方法
2. 断点时查看函数调用栈,对比 func (e entity) inc()和 func (e *entity) inc()

可以发现在使用包装方法时的函数栈多出一层
aababc
108 天前
@yicixin 我看了这个说法大致是这样的,T 类型实际上并不支持实现接口,在通过接口值调用方法的时候,都是通过 *T 来调用的
yicixin
108 天前
@aababc 是的,因为在编译期,interface 调用方法并不知道实现者的具体类型,可能实现者是一个大对象,也可能是一个小对象,所占用的内存是确定不了的,这样就无法确定当前要分配的函数栈大小。实际在 go 中,interface 里装的是指向实现者的指针,指针的大小是确定的,就可以确定当前要分配的函数栈大小,相应的,只能调用*T 的方法集。
此时不能将指针解引用再调用 T 的方法集,因为解引用后要占用的内存大小是不确定的
yicixin
108 天前
所以 Go 对 T 的方法集自动生成*T 的包装方法,完全是给接口调用提供的。
基于这点,可以理解为什么 go 不允许你对一个方法同时实现 T 和*T 两份,是因为怕你两个实现的逻辑不一样。
aababc
107 天前
@yicixin 嗯,这一段时间买了一本 深度探索 go 大概研究一下,感觉这一块欠缺的知识比较多

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1006449

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX