Tony Zhang
Tony Zhang

Reputation: 49

converting a method to a function with a pointer to the struct in Golang

I read some strange codes which convert a method to a function whose first argument is a pointer to this method's struct.

I write an example to demonstrate it:

package main

import "fmt"

type fooS struct{}

func (s *fooS) fooF(fooArg interface{}) {
    fmt.Println("fooF: ", fooArg)
}

type wowS struct {
    callback func(s *fooS, fooArg interface{})
}

func main() {
    wow := new(wowS)
    wow.callback = (*fooS).fooF // strange
    wow.callback(nil, 123)
}

Golang Playground Link

The example's syntax is strange but has no error.

Can any one tell me how these codes work or give me an official document about them?

Thanks:)

Upvotes: 2

Views: 3164

Answers (1)

user6169399
user6169399

Reputation:

Method expressions:

If M is in the method set of type T, T.M is a function that is callable as a regular function with the same arguments as M prefixed by an additional argument that is the receiver of the method.

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = TypeName | "(" "*" TypeName ")" | "(" ReceiverType ")" .

Consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.

type T struct {
  a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T

The expression

T.Mv

yields a function equivalent to Mv but with an explicit receiver as its first argument; it has signature

func(tv T, a int) int

That function may be called normally with an explicit receiver, so these five invocations are equivalent:

t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

Similarly, the expression

(*T).Mp 

yields a function value representing Mp with signature

func(tp *T, f float32) float32

For a method with a value receiver, one can derive a function with an explicit pointer receiver, so

(*T).Mv

yields a function value representing Mv with signature

func(tv *T, a int) int

Such a function indirects through the receiver to create a value to pass as the receiver to the underlying method; the method does not overwrite the value whose address is passed in the function call.

The final case, a value-receiver function for a pointer-receiver method, is illegal because pointer-receiver methods are not in the method set of the value type.

Function values derived from methods are called with function call syntax; the receiver is provided as the first argument to the call. That is, given f := T.Mv, f is invoked as f(t, 7) not t.f(7). To construct a function that binds the receiver, use a function literal or method value.

It is legal to derive a function value from a method of an interface type. The resulting function takes an explicit receiver of that interface type.


And see:
Go - difference between parameter and receiver
Is there a performance penalty for passing "this" by value in Go methods?
differences between pointer and value slice in for-range loop

Upvotes: 8

Related Questions