Go

What is the difference between a method value and a method expression? What does this print?

Question 38HardGo 1.22 to 1.25
type P struct{ X int }

func (p P) Get() int   { return p.X }
func (p *P) Set(v int) { p.X = v }

func main() {
	p := P{1}
	get := p.Get    // method value: a copy of p is bound NOW
	set := p.Set    // method value: &p is bound
	f := P.Get      // method expression: func(P) int
	g := (*P).Set   // method expression: func(*P, int)
	h := (*P).Get   // func(*P) int: legal, *P's method set includes Get
	// _ = P.Set    // compile error: invalid method expression P.Set (needs pointer receiver (*P).Set)

	set(5)
	fmt.Println(get(), p.X, f(p), h(&p))
	g(&p, 7)
	fmt.Println(p.X)
}

Output:

1 5 5 5
7

A method value, x.M, is a function with the receiver already bound. The receiver is evaluated and saved when the expression is evaluated. With a value receiver, that means a copy, so get() still returns 1 after set(5). With a pointer receiver on an addressable variable, &p is saved, so later changes are visible.

A method expression, T.M or (*T).M, turns the method into an ordinary function whose first parameter is the receiver. It follows method-set rules: (*P).Get works because the method set of *P includes value methods, but P.Set is rejected.

Where this matters:

  • Passing callbacks: http.HandleFunc("/", srv.handle) binds srv once. If handle has a value receiver, later changes to srv are invisible to the handler.
  • Evaluating a method value on a nil interface panics immediately, at the point where you write r.Read, not later when you call it.
  • A method value that escapes (stored, passed to a goroutine) allocates a closure. Method expressions don't.
  • Method expressions are handy for table-driven code: ops := map[string]func(*Stack){"pop": (*Stack).Pop}.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions