dongzhang5787 2018-07-09 04:41
浏览 37
已采纳

为什么可以将结构函数分配给其他结构的成员?

I was just messing around and wrote the below piece of code,

package main

import (
    "fmt"
)

type Person struct {
    name string
}

func (p Person) printName() {
    fmt.Println(p.name)
}

type Man struct {
    name string
    f func()
}

func main() {
    p := Person{name: "John"}
    m := Man{name: "Adam"}
    m.f = p.printName
    p.printName()
    m.f()
}

The above code results in the following output. This works across packages too.

John
John

So, here are my questions.

  1. Why does this work?
  2. Struct methods require receivers of the same type. How is the function still able to access members of the Person struct?
  3. What happens when m.f = p.printName is executed in the above example?
  • 写回答

3条回答 默认 最新

  • duanbei3704 2018-07-09 05:29
    关注

    This question deals mostly with receivers and may extend a bit to embedding. From the relevant section of the spec:

    A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and associates the method with the receiver's base type.

    MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .

    Receiver = Parameters .

    The receiver is specified via an extra parameter section preceding the method name. That parameter section must declare a single non-variadic parameter, the receiver. Its type must be of the form T or *T (possibly using parentheses) where T is a type name. The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be defined in the same package as the method. The method is said to be bound to the base type and the method name is visible only within selectors for type T or *T.

    A non-blank receiver identifier must be unique in the method signature. If the receiver's value is not referenced inside the body of the method, its identifier may be omitted in the declaration. The same applies in general to parameters of functions and methods.

    For a base type, the non-blank names of methods bound to it must be unique. If the base type is a struct type, the non-blank method and field names must be distinct.

    Given type Point, the declarations

    func (p *Point) Length() float64 {    
       return math.Sqrt(p.x * p.x + p.y *p.y) 
    }
    
    func (p *Point) Scale(factor float64) {   
       p.x *= factor  
       p.y *= factor
    }
    

    bind the methods Length and Scale, with receiver type *Point, to the base type Point.

    The type of a method is the type of a function with the receiver as first argument.

    For instance, the method Scale has type

    func(p *Point, factor float64)

    However, a function declared this way is not a method.

    Man has a field named f that is a function that takes no arguments and returns nothing. As we saw above golang internally treats

    func (p Person) printName()
    

    as

    func printName(p Person)
    

    and this can be considered as a function of no arguments when it acts on a Person struct (and it does because p is a Person and p.printName acts on p). Therefore it is allowed to be assigned to Man.f

    So the moment you assigned the f field on the Man struct to a function that has captured the Person instance with name "John" and reads the name from that, therefore you get effect of the second "John" being printed. The Man.name field has never come into play on that one.

    I suspect that what you would expect as normal behaviour can be achieved with struct embedding of Person into a Man.

    here is a playground link to demonstrate that

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 pyqt5tools安装失败
  • ¥15 mmdetection
  • ¥15 nginx代理报502的错误
  • ¥100 当AWR1843发送完设置的固定帧后,如何使其再发送第一次的帧
  • ¥15 图示五个参数的模型校正是用什么方法做出来的。如何建立其他模型
  • ¥100 描述一下元器件的基本功能,pcba板的基本原理
  • ¥15 STM32无法向设备写入固件
  • ¥15 使用ESP8266连接阿里云出现问题
  • ¥15 BP神经网络控制倒立摆
  • ¥20 要这个数学建模编程的代码 并且能完整允许出来结果 完整的过程和数据的结果