Attach behavior to types — value vs pointer receivers.
Methods and Receivers
A method is a function with a special "receiver" argument written before the name. It's how you attach behavior to types in Go.
type Rect struct{ W, H float64 }
func (r Rect) Area() float64 {
return r.W * r.H
}
That (r Rect) is the receiver — read it as "on a Rect, called r".
Value vs pointer receivers
You can declare a method on either a value or a pointer:
func (r Rect) Area() float64 { ... } // value receiver — gets a copy
func (r *Rect) Scale(k float64) { ... } // pointer receiver — can modify r
Rules of thumb:
- Use a pointer receiver if the method needs to modify the receiver, or if the struct is large enough that copying is wasteful, or if other methods on the type already use pointer receivers (be consistent).
- Use a value receiver for small, immutable types (
time.Time,Point).
Go automatically takes the address when needed: c.Inc() works even though Inc
expects *Counter. (You can also write (&c).Inc() — same thing.)
Methods aren't limited to structs
You can define methods on any named type, including aliases of built-in types:
You cannot define methods on types you don't own (e.g. int, or another
package's types). Wrap them in your own named type first.
A subtle interface trap
Method sets matter when you put a value behind an interface:
- A value of type
Thas only the methods defined with value receivers. - A pointer
*Thas both value-receiver and pointer-receiver methods.
type Greeter interface{ Greet() }
type Dog struct{}
func (d *Dog) Greet() {} // pointer receiver only
var g Greeter = Dog{} // ✗ compile error: Dog{} doesn't have Greet()
var g Greeter = &Dog{} // ✓ OK
If a method "doesn't exist" when you assign to an interface, double-check whether you're using a value or a pointer.