V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
aababc
V2EX  ›  Go 编程语言

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

  •  
  •   aababc · 112 天前 · 1412 次点击
    这是一个创建于 112 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    6 条回复    2024-01-26 10:04:03 +08:00
    PTLin
        1
    PTLin  
       112 天前
    就想象成解引用然后运行,没什么太特殊的。
    我以前这块记过点笔记看看能不能帮上你 https://imgur.com/a/EUVihfK
    yicixin
        2
    yicixin  
       94 天前
    是不是说反了,应该是 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
        3
    aababc  
    OP
       93 天前
    @yicixin 我看了这个说法大致是这样的,T 类型实际上并不支持实现接口,在通过接口值调用方法的时候,都是通过 *T 来调用的
    yicixin
        4
    yicixin  
       93 天前
    @aababc 是的,因为在编译期,interface 调用方法并不知道实现者的具体类型,可能实现者是一个大对象,也可能是一个小对象,所占用的内存是确定不了的,这样就无法确定当前要分配的函数栈大小。实际在 go 中,interface 里装的是指向实现者的指针,指针的大小是确定的,就可以确定当前要分配的函数栈大小,相应的,只能调用*T 的方法集。
    此时不能将指针解引用再调用 T 的方法集,因为解引用后要占用的内存大小是不确定的
    yicixin
        5
    yicixin  
       93 天前
    所以 Go 对 T 的方法集自动生成*T 的包装方法,完全是给接口调用提供的。
    基于这点,可以理解为什么 go 不允许你对一个方法同时实现 T 和*T 两份,是因为怕你两个实现的逻辑不一样。
    aababc
        6
    aababc  
    OP
       93 天前
    @yicixin 嗯,这一段时间买了一本 深度探索 go 大概研究一下,感觉这一块欠缺的知识比较多
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3578 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 10:40 · PVG 18:40 · LAX 03:40 · JFK 06:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.