今天看了一个说法,*T
类型会在编译期间生成所有 T
类型的同名包装方法, 大家有理解这句话是啥意思的吗? 如果生成同名包装方法,针对 T
类型中有副作用的方法(不会真的影响原值),会在 *T
中真的对原值产生影响吗。
1
PTLin 321 天前
就想象成解引用然后运行,没什么太特殊的。
我以前这块记过点笔记看看能不能帮上你 https://imgur.com/a/EUVihfK |
2
yicixin 302 天前
是不是说反了,应该是 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() 可以发现在使用包装方法时的函数栈多出一层 |
4
yicixin 302 天前
@aababc 是的,因为在编译期,interface 调用方法并不知道实现者的具体类型,可能实现者是一个大对象,也可能是一个小对象,所占用的内存是确定不了的,这样就无法确定当前要分配的函数栈大小。实际在 go 中,interface 里装的是指向实现者的指针,指针的大小是确定的,就可以确定当前要分配的函数栈大小,相应的,只能调用*T 的方法集。
此时不能将指针解引用再调用 T 的方法集,因为解引用后要占用的内存大小是不确定的 |
5
yicixin 302 天前
所以 Go 对 T 的方法集自动生成*T 的包装方法,完全是给接口调用提供的。
基于这点,可以理解为什么 go 不允许你对一个方法同时实现 T 和*T 两份,是因为怕你两个实现的逻辑不一样。 |