Reputation: 41
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.
m.f = p.printName
is executed in the above example?Upvotes: 3
Views: 800
Reputation: 1372
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
Upvotes: 4
Reputation: 8232
A method, in Go, is basically a function with a receiver
. That receiver is wraped by the compiler, and beyond that, there is nothing more different from a normal function. That means, at anywhere, the method always gets the receiver which it is bound to, no matter how you call it, assign it to another variable or anythiing else.
In your code, f
is not a method of type Man
. It is merely a field of type func()
. You can set to anything that match the signature, and the function will know nothing about Man
or its instance. That means, m.f
has no knowledge of m
and no access to m.name
or any other field of m
.
And a note, you can call methods like: Person.PrintName(p)
where p is of type Person
.
Upvotes: 2
Reputation: 302
function in Golang is also a value, it can be passed as parameter or assigned by other function too, method in Golang is also a function, with one different, it has receiver in it's value, so when use assign a method to a function (which have same signature) it's refer to the receiver and executing code in that method to destination function
Upvotes: -1