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

go 结构体方法的困惑

  •  
  •   yujianwjj · 2021-03-23 21:15:33 +08:00 · 2774 次点击
    这是一个创建于 1374 天前的主题,其中的信息可能已经有所发展或是发生改变。
    type A struct {
    	i int
    }
    
    func (a *A) Set(i int) {
    	a.i = i
    }
    
    func TestInherit(t *testing.T) {
    	var a = A{3}
    	a.Set(4)
        t.Log(a.i)  // 输出 4
    }
    

    这里可以通过非指针的 a 调用 Set 函数,并且 Set 的值是有效地。

    第一个疑问:go 在调用方法时,会先将接收器的值复制一份,然后在这个副本上执行方法。我的理解是 a 复制了一份,在新的 a 上面修改不应该影响原来的 a 。

    type Set interface {
    	Set(int)
    }
    
    type A struct {
    	i int
    }
    
    func (a *A) Set(i int) {
    	a.i = i
    }
    
    func TestInherit(t *testing.T) {
    	var _ Set = A{}		// 这里编译不通过,提示没有实现 Set 方法。
    }
    

    第二个疑问,为啥上面的例子能调用 a.Set(),为啥这里又报错说没有实现 Set 方法。

    第 1 条附言  ·  2021-03-24 09:51:15 +08:00
    明白了,原来有隐式转换。
    12 条回复    2021-03-25 06:54:35 +08:00
    SuperMild
        1
    SuperMild  
       2021-03-23 21:38:13 +08:00
    https://tour.golang.org/methods/6

    That is, as a convenience, Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.
    thefack
        2
    thefack  
       2021-03-23 21:41:32 +08:00
    第一个问题是编译器给你补充了引用:`&a.Set(4)`
    第二个问题对照第一个问题,实现 Set 的是 &A,不是 A,所以报错
    linvon
        3
    linvon  
       2021-03-23 21:43:15 +08:00
    一:如果是值接收器确实是做一次值拷贝,但你这个方法是指针接收器,go 会对 a 做取地址,在执行指针接收器的方法,就会修改值
    二:Set 方法实现的是指针接收器,A{}是一个值类型,并没有实现 Set 方法

    可以去搜一下 go 的值接收器和指针接收器区别
    kiripeng
        4
    kiripeng  
       2021-03-23 21:52:40 +08:00
    type A struct {
    i int
    }

    func (a *A) Set(i int) {
    a.i = i
    }

    等于 func Set(a *A,i int) 这个准确来说是实现了指针形式的方法集
    如果不用指针 var _ Set =&A{}而是使用 var _ Set =A{} 就会出现这个问题就是了,因为这时候是去 A 找实现了非地址值的方法集,然后找不到,可惜的是对于 golang 来说我觉得他限制了地址值和非地址值嵌入只能一种最大的目的应该是防止出现用到后面会出事,不知道哪个是哪个,防止地址值的方法集和非地址值的方法集混淆。
    var _ Set =A{} 如果要行得通就是 func (a A) Set(i int) {
    a.i = i
    }

    func (a *A) Set(i int) {
    a.i = i
    }
    而对于这个,a.set 其实就是转化成 (&a,)了,这个你可以试下如果实现的是

    func (a A) Set(i int) {
    a.i = i
    }
    用空指针去调用他就会爆出
    main.(*A).Set(...)
    keepeye
        5
    keepeye  
       2021-03-23 22:00:33 +08:00
    第一个是值拷贝没错,但是调方法的时候隐式转换成指针接收了,但不能说 a 实现了 Set 接口。。所以还是代码不规范的问题,正常因该不会这么写
    george404
        6
    george404  
       2021-03-24 07:09:54 +08:00
    试试看

    ```
    func (a *A) Set(i int) {
    a.i = i
    }
    ```
    改成

    ```
    func (a *A) Set(i int) {
    a.i = i
    }
    ```
    你看看你还能修改值了
    love2020
        7
    love2020  
       2021-03-24 08:31:00 +08:00
    为什么不先看教程。。。
    liuxey
        8
    liuxey  
       2021-03-24 08:40:21 +08:00
    @froyobin #6 你这.
    ly020044
        9
    ly020044  
       2021-03-24 08:41:12 +08:00
    哎呀我的大宝贝,为啥不先看一下教程?好好看一下指针吧
    dong568789
        10
    dong568789  
       2021-03-24 10:04:26 +08:00
    6 楼在说啥?
    george404
        12
    george404  
       2021-03-25 06:54:35 +08:00
    @dong568789 擦,忘了吧第二个的*A 的指针去掉。。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5478 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 03:38 · PVG 11:38 · LAX 19:38 · JFK 22:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.