Golang Go语言中关于 T 和 *T 方法集的一点疑问
今天看了一个说法,*T
类型会在编译期间生成所有 T
类型的同名包装方法, 大家有理解这句话是啥意思的吗? 如果生成同名包装方法,针对 T
类型中有副作用的方法(不会真的影响原值),会在 *T
中真的对原值产生影响吗。
Golang Go语言中关于 T 和 *T 方法集的一点疑问
就想象成解引用然后运行,没什么太特殊的。
我以前这块记过点笔记看看能不能帮上你 https://imgur.com/a/EUVihfK
更多关于Golang Go语言中关于 T 和 *T 方法集的一点疑问的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是不是说反了,应该是 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()
可以发现在使用包装方法时的函数栈多出一层
我看了这个说法大致是这样的,T 类型实际上并不支持实现接口,在通过接口值调用方法的时候,都是通过 *T 来调用的
是的,因为在编译期,interface 调用方法并不知道实现者的具体类型,可能实现者是一个大对象,也可能是一个小对象,所占用的内存是确定不了的,这样就无法确定当前要分配的函数栈大小。实际在 go 中,interface 里装的是指向实现者的指针,指针的大小是确定的,就可以确定当前要分配的函数栈大小,相应的,只能调用*T 的方法集。
此时不能将指针解引用再调用 T 的方法集,因为解引用后要占用的内存大小是不确定的
所以 Go 对 T 的方法集自动生成T 的包装方法,完全是给接口调用提供的。
基于这点,可以理解为什么 go 不允许你对一个方法同时实现 T 和T 两份,是因为怕你两个实现的逻辑不一样。
嗯,这一段时间买了一本 深度探索 go 大概研究一下,感觉这一块欠缺的知识比较多
在Go语言中,关于类型T和指针类型*T的方法集,确实是一个常见的疑问点。下面我将尽量简洁明了地解答这个疑问。
首先,需要明确的是,Go语言中的方法是可以绑定到值类型(T)或者指针类型(T)上的。类型T的方法集仅包含绑定到该值类型的方法,而指针类型T的方法集则包含绑定到该指针类型的方法以及通过该指针可以访问的值类型的方法(即可以通过指针解引用后调用的方法)。
这意味着,如果你有一个值x类型为T,那么你可以调用T的方法集里的方法。但如果你有一个指向T的指针p(即T类型),你不仅可以调用T的方法集里的方法,还可以调用T的方法集里的方法,因为Go会自动对指针进行解引用以调用值类型的方法。
然而,有一个重要的注意事项:如果你需要在方法中修改接收者的字段,那么你必须将方法绑定到指针类型上。因为值传递是Go的函数调用机制,如果你传递一个值类型作为接收者,那么在方法内部对这个接收者的修改将不会影响到原始值。相反,如果你传递一个指针类型作为接收者,那么你就可以在方法内部修改这个指针指向的值,这些修改将反映到原始值上。
希望这个解释能够解答你的疑问。如果还有其他问题或需要进一步的解释,请随时提问。