Methods and Receivers
Overview
Methods are functions with a receiver argument, allowing you to define behavior on types.
Method Syntax
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
rect := Rectangle{10, 5}
area := rect.Area() // 50Value vs Pointer Receivers
Value Receiver
func (r Rectangle) Scale(factor float64) Rectangle {
return Rectangle{r.Width * factor, r.Height * factor}
}
// Original unchanged
r := Rectangle{10, 5}
scaled := r.Scale(2) // New rectanglePointer Receiver
func (r *Rectangle) ScaleInPlace(factor float64) {
r.Width *= factor
r.Height *= factor
}
// Original modified
r := Rectangle{10, 5}
r.ScaleInPlace(2) // r is now {20, 10}When to Use Pointer Receivers
Use pointer receiver when: - Method needs to modify the receiver - Receiver is a large struct (avoid copying) - Consistency: if any method needs pointer, use pointer for all
type Buffer struct {
data []byte
}
func (b *Buffer) Write(p []byte) {
b.data = append(b.data, p...)
}
func (b *Buffer) String() string {
return string(b.data) // Even though it doesn't modify, use * for consistency
}Methods on Any Type
type MyInt int
func (m MyInt) Double() MyInt {
return m * 2
}
n := MyInt(5)
n.Double() // 10Automatic Dereferencing
Go automatically handles * and & for method calls:
r := Rectangle{10, 5}
(&r).ScaleInPlace(2) // Explicit pointer
r.ScaleInPlace(2) // Go handles it automatically
ptr := &Rectangle{10, 5}
(*ptr).Area() // Explicit dereference
ptr.Area() // Go handles it automaticallyEmbedding and Method Promotion
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "..."
}
type Dog struct {
Animal // Embedded
Breed string
}
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
d.Speak() // Promoted method: "..."
d.Name // Promoted field: "Rex"Method Override
func (d Dog) Speak() string {
return "Woof!"
}
d.Speak() // "Woof!" (Dog's method)
d.Animal.Speak() // "..." (Animal's method)Summary
| Receiver | Use Case |
|---|---|
(t T) |
Read-only, small types |
(t *T) |
Modify state, large types |
| Feature | Description |
|---|---|
| Auto-deref | Go handles * and & |
| Embedding | Methods are promoted |
| Override | Inner type’s method shadows embedded |
More examples
Example: value receiver cannot mutate
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
type Point struct{ X, Y int }
func (p Point) Move(dx, dy int) {
p.X += dx
p.Y += dy
}
func main() {
p := Point{1, 2}
p.Move(10, 10)
fmt.Printf("after value Move: %+v\n", p)
}Expected:
after value Move: {X:1 Y:2}
Example: pointer receiver mutates
Save as main.go and go run . (with go mod init example if needed).
package main
import "fmt"
type Point struct{ X, Y int }
func (p *Point) Move(dx, dy int) {
p.X += dx
p.Y += dy
}
func main() {
p := Point{1, 2}
p.Move(10, 10)
fmt.Printf("after pointer Move: %+v\n", p)
}Expected:
after pointer Move: {X:11 Y:12}
Runnable example
Save as main.go. Then:
go mod init example
go run .package main
import "fmt"
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Value receiver: returns a new value; original unchanged.
func (r Rectangle) Scale(factor float64) Rectangle {
return Rectangle{r.Width * factor, r.Height * factor}
}
// Pointer receiver: mutates in place.
func (r *Rectangle) ScaleInPlace(factor float64) {
r.Width *= factor
r.Height *= factor
}
type Animal struct{ Name string }
func (a Animal) Speak() string { return "..." }
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string { return "Woof!" }
func main() {
r := Rectangle{Width: 10, Height: 5}
fmt.Println("area:", r.Area())
scaled := r.Scale(2)
fmt.Printf("after Scale: orig=%v scaled=%v\n", r, scaled)
r.ScaleInPlace(2) // Go passes &r automatically
fmt.Printf("after ScaleInPlace: %v\n", r)
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println("promoted Name:", d.Name)
fmt.Println("Dog.Speak:", d.Speak())
fmt.Println("Animal.Speak:", d.Animal.Speak())
}Expected output:
area: 50
after Scale: orig={10 5} scaled={20 10}
after ScaleInPlace: {20 10}
promoted Name: Rex
Dog.Speak: Woof!
Animal.Speak: ...
What to notice: Value receivers copy; pointer receivers share the same Rectangle. Embedding promotes Name and Speak, and Dog.Speak shadows the embedded method.
Try next: Give Rectangle a String() string method and print with %v vs %s, or add a value-receiver ScaleInPlace (wrongly) and observe that the caller’s fields no longer change.