Search code examples
gomemory-addressfuncreflect

Explain the printed values of method expressions


The following code tries to show the address of a method associated to struct.

package  main

import (
    "fmt"
    "reflect"
)

type II interface {
    Callme()
}

type Str struct {
    I int
    S string
}

func (s *Str) Callme () {
    fmt.Println("it is me")
}

func main() {
    s0 := &Str{}
    t := reflect.TypeOf(s0)
    v := reflect.ValueOf(s0)
    fmt.Println("Callme ", s0.Callme)   //real address ?
    fmt.Println(t.Method(0).Name, v.Method(0))    //real address ?

    s1 := &Str{}
    t1 := reflect.TypeOf(s1)
    v1 := reflect.ValueOf(s1)
    fmt.Println("Callme ", s1.Callme)   //real address ?
    fmt.Println(t1.Method(0).Name, v1.Method(0))    //real address ?
}

The output is :

Callme  0x4bc2d0
Callme 0x4ab2c0
Callme  0x4bc2d0
Callme 0x4ab2c0

So I have two questions:

  • First, why these statements do not show identical values ?

    fmt.Println("Callme ", s0.Callme)
    fmt.Println(t.Method(0).Name, v.Method(0))
    
  • Second, why these statements show identical values ?

    fmt.Println(t.Method(0).Name, v.Method(0))    
    fmt.Println(t1.Method(0).Name, v1.Method(0)) 
    

Solution

  • The fmt package calls Value.Pointer to get function addresses.

    Let's look at an example of what Value.Pointer returns for the functions:

    s0 := &Str{}
    v0 := reflect.ValueOf(s0)
    fmt.Printf("s0.Callme: %0x %0x\n", reflect.ValueOf(s0.Callme).Pointer(), s0.Callme)
    fmt.Printf("v0.Method(0) %0x %0x\n", v0.Method(0).Pointer(), v0.Method(0))
    
    s1 := &Str{}
    v1 := reflect.ValueOf(s1)
    fmt.Printf("s1.Callme %x %x\n", reflect.ValueOf(s1.Callme).Pointer(), s1.Callme)
    fmt.Printf("v1.Method(0) %x %x\n", v1.Method(0).Pointer(), v1.Method(0))
    

    The output is:

    s0.Callme: 105240 105240
    v0.Method(0) eee60 eee60
    s1.Callme 105240 105240
    v1.Method(0) eee60 eee60
    

    This matches the pattern shown in the question.

    The function related code for Value.Pointer is:

        if v.flag&flagMethod != 0 {
            // As the doc comment says, the returned pointer is an
            // underlying code pointer but not necessarily enough to
            // identify a single function uniquely. All method expressions
            // created via reflect have the same underlying code pointer,
            // so their Pointers are equal. The function used here must
            // match the one used in makeMethodValue.
            f := methodValueCall
            return **(**uintptr)(unsafe.Pointer(&f))
        }
        p := v.pointer()
        // Non-nil func value points at data block.
        // First word of data block is actual code.
        if p != nil {
            p = *(*unsafe.Pointer)(p)
        }
        return uintptr(p)
    

    A reflect.Value created through a method expression in the reflect API has the flagMethod method bit set. As the comment states and the code shows, the Pointer method returns the same value for all method expressions created this way.

    The reflect.Value created by relect.ValueOf(s1.Callme) does not have the flagMethod method bit set. In this case, the function returns the pointer to the actual code.

    The output of this program shows all of the combinations:

    type StrA struct {
        I int
        S string
    }
    
    func (s *StrA) Callme() {
        fmt.Println("it is me")
    }
    
    type StrB struct {
        I int
        S string
    }
    
    func (s *StrB) Callme() {
        fmt.Println("it is me")
    }
    
    s0A := &StrA{}
    v0A := reflect.ValueOf(s0A)
    s1A := &StrA{}
    v1A := reflect.ValueOf(s0A)
    
    fmt.Println("s0A.Callme ", reflect.ValueOf(s0A.Callme).Pointer())
    fmt.Println("v0A.Method(0) ", v0A.Method(0).Pointer())
    fmt.Println("s1A.Callme ", reflect.ValueOf(s1A.Callme).Pointer())
    fmt.Println("v1A.Method(0) ", v1A.Method(0).Pointer())
    
    s0B := &StrB{}
    v0B := reflect.ValueOf(s0B)
    s1B := &StrB{}
    v1B := reflect.ValueOf(s0B)
    
    fmt.Println("s0B.Callme ", reflect.ValueOf(s0B.Callme).Pointer())
    fmt.Println("v0B.Method(0) ", v0B.Method(0).Pointer())
    fmt.Println("s1B.Callme ", reflect.ValueOf(s1B.Callme).Pointer())
    fmt.Println("v1B.Method(0) ", v1B.Method(0).Pointer())
    

    Output:

    s0A.Callme  1061824
    v0A.Method(0)  978528
    s1A.Callme  1061824
    v1A.Method(0)  978528
    s0B.Callme  1061952
    v0B.Method(0)  978528
    s1B.Callme  1061952
    v1B.Method(0)  978528
    

    We can observe that Value.Pointer returns the same value for all method expressions created through the reflect API. This includes methods on different types.

    We can also observe that Value.Pointer returns the same value for all method expressions on a given type and method. This is true for method expressions bound to different values.

    The Value.Pointer documentation says:

    If v's Kind is Func, the returned pointer is an underlying code pointer, but not necessarily enough to identify a single function uniquely. The only guarantee is that the result is zero if and only if v is a nil func Value.

    Given this, an application cannot reliably use Value.Pointer or printed values through the fmt package to compare functions and methods.