There was no livestream today, as I was a bit under the weather. I intend to be back live streaming next Monday as usual
Method expressions
If
M
is in the method set of typeT
,T.M
is a function that is callable as a regular function with the same arguments asM
prefixed by an additional argument that is the receiver of the method.MethodExpr = ReceiverType "." MethodName . ReceiverType = Type .
This might seem a bit mundane at first. But there’s something subtle, going on here, that surprises most people when they first encounter it.
Consider a struct type
T
with two methods,Mv
, whose receiver is of typeT
, andMp
, 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 signaturefunc(tv T, a int) int
If you’re like most, you’re so accustomed to calling methods on instantiated values, that you’ve never considered what the full function signature of a method looks like when separated from the receiver—or that the concept even makes sense.
But that’s essentially what we’ve done here:
x := T.Mv
notMv := func(T, int) int {
return 0
}
x = notMv // This assignment is valid, because notMv is of the same type as x, aka `func(T, 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)
Tomorrow we’ll complete the discussion of the second half of the example, with the pointer receiver.
Quotes from The Go Programming Language Specification Version of August 2, 2023