Methods and Receivers

Updated

July 30, 2026

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()  // 50

Value 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 rectangle

Pointer 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()  // 10

Automatic 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 automatically

Embedding 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.