Sunday, August 16, 2026
Go Method Values vs Method Expressions: What's Actually the Difference?
Posted by

A method value x.M captures x immediately. The result is a function whose signature is the method without the receiver, mux.HandleFunc("/users", srv.ListUsers). A method expression T.M or (*T).M does not bind an instance. The receiver is argument one. Use that when the action is chosen first and the object later.
The failure
You store Counter.Value and call it like a method value:
package main
import "fmt"
type Counter struct{ n int }
func (c Counter) Value() int { return c.n }
func main() {
c := Counter{n: 5}
fn := Counter.Value
fmt.Println(fn()) // compile error: not enough arguments
}
The other direction: fn2 := c.Value then fn2(c) is too many arguments.
Value vs pointer is a second trap. g.Say with a value receiver snapshots g. Change g.prefix later; the stored func still says "hello". A method with a pointer receiver is only reachable through the pointer type, so write (*T).Inc, not T.Inc. T{}.PtrMethod as a method value fails: the composite is not addressable.
Why this happens
Every method is already a function with the receiver as the first parameter. x.M() is T.M(x) (or (*T).M(&x)). The two expressions just choose when that first argument is supplied.
- Method value: evaluate
xnow, hide it inside a closure. Callers never pass the receiver. Reassigning the variablexlater does not retarget the func; a pointer receiver still sees mutations through the captured address. - Method expression: no instance yet.
Person.Lessisfunc(Person, Person) bool. A mapmap[string]func(*TaskRunner)stores(*TaskRunner).Build.
Parentheses matter: (*Counter).Increment, never *Counter.Increment.
The fix
Rule of thumb: if you already have the receiver, use a method value. If the receiver only arrives when you call the function, use a method expression.
type Greeter struct{ prefix string }
func (g Greeter) Say(name string) string {
return g.prefix + " " + name
}
g := Greeter{prefix: "hello"}
say := g.Say // method value: func(string) string
fmt.Println(say("ana")) // hello ana
fn := Greeter.Say // method expression: func(Greeter, string) string
fmt.Println(fn(g, "ana"))
actions := map[string]func(*TaskRunner){
"build": (*TaskRunner).Build,
}
actions["build"](runner)
Need the method to follow a variable that gets reassigned? Write a closure that reads the variable at call time. A method value will not.