The behavior of the function variable which points to the struct method

153 views
Skip to first unread message

E Z

unread,
Aug 10, 2021, 2:01:10 PM8/10/21
to golang-nuts
I feel confused when I use the function pointer which point to the struct method. Here is the test code:

/***
package main

type Zoo struct {
    Animal string
}

func (z Zoo) Display(){
    fmt.Printf("Current animal is:%s\n", z.Animal)
}

func main(){
    gz := &Zoo{
        Animal: "Monkey",
    }

    pf := gz.Display
    pf()                                       //display "Current animal is Monkey"
    gz.Animal="Tiger"
    pf()                                        //display "Current animal is Monkey"
    gz.Display()                         //display "Current animal is Tiger"
}
***/
As the code is shown above,  even though I changed the value of the member field of the Zoo instance gz,  the function pointer call that followed still prints the unmodified value.

Can someone help explain why? In my opinion, The function pointer pf should bind to the instance gz and its method(note that gz here is a pointer variable),  if so anytime I change the value of the variable gz,  pf should reflect the changes.  

Thanks,
Ethan

burak serdar

unread,
Aug 10, 2021, 3:07:26 PM8/10/21
to E Z, golang-nuts
On Tue, Aug 10, 2021 at 12:01 PM E Z <lege...@gmail.com> wrote:
I feel confused when I use the function pointer which point to the struct method. Here is the test code:

/***
package main

type Zoo struct {
    Animal string
}

func (z Zoo) Display(){
    fmt.Printf("Current animal is:%s\n", z.Animal)
}

This method has a value receiver. When pf is assigned to gz.Display, it is assigned with its receiver, which is a copy of gz.

Change the method to func (z *Zoo) Display(), and it will work as you expect.
 

func main(){
    gz := &Zoo{
        Animal: "Monkey",
    }

    pf := gz.Display
    pf()                                       //display "Current animal is Monkey"
    gz.Animal="Tiger"
    pf()                                        //display "Current animal is Monkey"
    gz.Display()                         //display "Current animal is Tiger"
}
***/
As the code is shown above,  even though I changed the value of the member field of the Zoo instance gz,  the function pointer call that followed still prints the unmodified value.

Can someone help explain why? In my opinion, The function pointer pf should bind to the instance gz and its method(note that gz here is a pointer variable),  if so anytime I change the value of the variable gz,  pf should reflect the changes.  

Thanks,
Ethan

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/60c6523c-dbce-4530-aa51-06f2c1223ca8n%40googlegroups.com.

E Z

unread,
Aug 10, 2021, 5:50:00 PM8/10/21
to golang-nuts
It works when I changed the code as your suggested. That's great, thanks.

And I'm still a little confused here, you know when we use the struct method directly, it is only when the function is called that the type of receiver determines whether the passed struct is a pointer or a copied value. But when using a function pointer, why does it decide whether to bind a pointer or a copied value at the time of assignment, but not at the time of function called? 

It seems that these two behaviors are not consistent. Is there any benefit to doing so? I didn't find much information on this topic on Google. Is there any extended reading on this topic?

Dan Kortschak

unread,
Aug 10, 2021, 5:55:06 PM8/10/21
to golan...@googlegroups.com
On Tue, 2021-08-10 at 14:50 -0700, E Z wrote:
> It works when I changed the code as your suggested. That's great,
> thanks.
>
> And I'm still a little confused here, you know when we use the struct
> method directly, it is only when the function is called that the type
> of receiver determines whether the passed struct is a pointer or a
> copied value. But when using a function pointer, why does it decide
> whether to bind a pointer or a copied value at the time of
> assignment, but not at the time of function called?
>
> It seems that these two behaviors are not consistent. Is there any
> benefit to doing so? I didn't find much information on this topic on
> Google. Is there any extended reading on this topic?
>

When you do this, `pf := gz.Display', you are capturing the state of gz
as it is. This fixes the animal.

Dan


burak serdar

unread,
Aug 10, 2021, 6:05:03 PM8/10/21
to E Z, golang-nuts
On Tue, Aug 10, 2021 at 3:50 PM E Z <lege...@gmail.com> wrote:
It works when I changed the code as your suggested. That's great, thanks.

And I'm still a little confused here, you know when we use the struct method directly, it is only when the function is called that the type of receiver determines whether the passed struct is a pointer or a copied value. But when using a function pointer, why does it decide whether to bind a pointer or a copied value at the time of assignment, but not at the time of function called? 

It seems that these two behaviors are not consistent. Is there any benefit to doing so? I didn't find much information on this topic on Google. Is there any extended reading on this topic?

It is consistent. The pointer to function assignment captures the current receiver.

func (z Zoo) Display()
func (z *Zoo) DisplayPtr()

fptr:=gz.Display  --> Captures gz, since Display gets gz by value, captures gz by value

fptr2:= gz.DisplayPtr -> Captures gz, since DisplayPtr gets gz by reference, captures *gz

That is:

gz:=&Zoo{}
fptr2:=gz.DisplayPtr
gz=&Zoo{}
fptr2() -> This will call the DisplayPtr with the first value of gz, not the second

 

E Z

unread,
Aug 10, 2021, 6:54:05 PM8/10/21
to golang-nuts
" The pointer to function assignment captures the current receiver." 
I quite agree with your above conclusion, and the test results also prove it. That seems to be the way it's designed right now, but what I find a little hard to understand here is why it's not designed as "The pointer to function assignment captures the current variable state."

In my opinion, if the function assignment captures the current variable, not the receiver, it seems more acceptable. I wrote an example to demo it through closure as the following:
/***
type Zoo struct {
    Animal string
}

func (z Zoo) Display(){
    fmt.Printf("Current animal is:%s\n", z.Animal)
}

func ZooDisplay(cur *Zoo) func() {
    return func(){
        cur.Display()

    }
}
func main(){
    gz := &Zoo{
        Animal: "Monkey",
    }
    pf := gz.Display
    pf2 := ZooDisplay(gz)


    pf()                               //display "Current animal is Monkey"
    pf2()                             //display "Current animal is Monkey"
    gz.Display()                //display "Current animal is Monkey"


    gz.Animal="Tiger" 

    pf()                             //display "Current animal is Monkey"
    pf2()                           //display "Current animal is Tiger"

    gz.Display()              //display "Current animal is Tiger"
}
***/

`ZooDisplay` is a closure to capture the current variable state, here it's `gz`,   and if we do so, the output of `pf2()` will always same as the output of  `gz.Display()`. This should make it easier to understand and less error-prone. 
It's a little weird that the second `pf()` and `gz.Display()` get different results from the current design. I don't think we want to get different results later on just because of a simple assignment(`pf := gz.Display`).

Dan Kortschak

unread,
Aug 10, 2021, 7:23:02 PM8/10/21
to golan...@googlegroups.com
On Tue, 2021-08-10 at 15:54 -0700, E Z wrote:
> I quite agree with your above conclusion, and the test results also
> prove it. That seems to be the way it's designed right now, but what
> I find a little hard to understand here is why it's not designed as
> "The pointer to function assignment captures the current variable
> state."

But that is exactly what it does do.

Dan


Brian Candler

unread,
Aug 11, 2021, 4:23:44 AM8/11/21
to golang-nuts
On Tuesday, 10 August 2021 at 22:50:00 UTC+1 lege...@gmail.com wrote:
And I'm still a little confused here, you know when we use the struct method directly, it is only when the function is called that the type of receiver determines whether the passed struct is a pointer or a copied value. But when using a function pointer, why does it decide whether to bind a pointer or a copied value at the time of assignment, but not at the time of function called? 


Neither is true.

In go, *all* arguments are passed by value.  If the argument is a struct, then the struct is copied.  If the argument is a pointer, then the pointer is copied.

Ignoring methods for a moment, just consider these simple functions:

func Display1(z Zoo) { ... }
func Display2(z *Zoo) { ... }

v := Zoo{....}
Display1(v)   # v is copied
vp := &Zoo
Display2(vp)  # vp is copied

These are identical, consistent behaviours.

So now onto "pointer to function".  You are thinking like C.  In Go there is no meaningful "pointer to function", there are just "function values":

Internally of course, a function value will contain some sort of pointer to the code, just as a string value contains a pointer to the string, and a slice value contains a pointer to the slice backing array. (In the latter two cases the pointer may be nil if the len or cap is zero, and the overall value is still valid).  When you pass a string or a slice to a function, you are copying this structure with its embedded pointer/len/cap,  These pointers are implementation details, and are not directly accessible to the user program - at least not in a "safe" way.

Whether the function takes zero arguments, one or more arguments, whether those arguments are structs or pointers or chans or whatever, affects the *type* of the value and hence how you call it, but otherwise a function value is just a value.

So finally we get to methods:

If you take a method value (pf := gz.Display), you are just getting a function value, where the special first argument of the function has been bound ("curried") to some value.

If the method takes a "Zoo" receiver, then the value is bound to a copy of the Zoo value.  If the method takes a "*Zoo" receiver, then the value is bound to a copy of the pointer-to-Zoo value.  Again, this is 100% consistent.  The function always receives a copy of the value, of the type of the argument.

There is just one bit of magic, which is the automatic referencing and dereferencing.  Very roughly speaking: if a method takes a *Zoo but you apply it to a Zoo value, or vice versa, the value is converted from Zoo to pointer-to-Zoo or vice versa as required.

But the value which is received by the method is always of the type it declares: func (z Zoo) Display() always takes a copy of a Zoo value, and func (z *Zoo) Display() always takes a copy of a pointer-to-Zoo value.  The value is always copied, and this is done at the time the method value is created, not the time at which it is called (which may be never, or may be many times).

Brian Candler

unread,
Aug 11, 2021, 4:28:08 AM8/11/21
to golang-nuts
On Wednesday, 11 August 2021 at 09:23:44 UTC+1 Brian Candler wrote:
v := Zoo{....}
Display1(v)   # v is copied
vp := &Zoo
Display2(vp)  # vp is copied


Correction: vp := &v  or  vp := &Zoo{...}
 
These are identical, consistent behaviours.

So now onto "pointer to function".  You are thinking like C.  In Go there is no meaningful "pointer to function", there are just "function values":


Correction: that got mangled in copy-paste, it should say

E Z

unread,
Aug 11, 2021, 1:59:57 PM8/11/21
to golang-nuts
Thank you so much. 

Your explanation makes my understanding of this problem more and more clear.

However, since I have been a C programmer for a long time, I still don't understand the implementation of the function variable in Golang very well. I think I need to do some inspection about function variable in Golang to know more details.

Sean Liao

unread,
Aug 11, 2021, 2:16:47 PM8/11/21
to golang-nuts
Perhaps this section of the spec will help: https://golang.org/ref/spec#Method_declarations

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

So there's only a single copy of the function code, but the argument passed to it is copied
From your original example: `gz.Display() == Zoo.Display(*gz)`

Reply all
Reply to author
Forward
0 new messages