Skip to main content
Go supports methods defined on struct types. Methods are functions with a special receiver argument that appears between the func keyword and the method name.

Basic method syntax

package main

import "fmt"

type rect struct {
    width, height int
}

// This area method has a receiver type of *rect.
func (r *rect) area() int {
    return r.width * r.height
}

// Methods can be defined for either pointer or value receiver types.
// Here's an example of a value receiver.
func (r rect) perim() int {
    return 2*r.width + 2*r.height
}

func main() {
    r := rect{width: 10, height: 5}

    // Call the methods defined for our struct.
    fmt.Println("area: ", r.area())
    fmt.Println("perim:", r.perim())

    // Go automatically handles conversion between values and pointers
    // for method calls. You may want to use a pointer receiver to avoid
    // copying on method calls or to allow the method to mutate the struct.
    rp := &r
    fmt.Println("area: ", rp.area())
    fmt.Println("perim:", rp.perim())
}
Output:
area:  50
perim: 30
area:  50
perim: 30

Pointer vs value receivers

func (r *rect) area() int {
    return r.width * r.height
}
Use when:
  • Method needs to modify the receiver
  • Receiver is a large struct (avoid copying)
  • Consistency (if some methods need pointers, use for all)

Automatic conversions

Go automatically handles conversions between values and pointers for method calls:
r := rect{width: 10, height: 5}
rp := &r

// All of these work:
r.area()   // Go converts to &r automatically
rp.area()  // Direct pointer call
r.perim()  // Direct value call
rp.perim() // Go dereferences automatically
You don’t need to worry about whether you have a value or pointer when calling methods — Go handles the conversion for you.

When to use pointer receivers

Modifying the receiver

func (r *rect) scale(factor int) {
    r.width *= factor
    r.height *= factor
}

Large structs

type BigStruct struct {
    // Many fields...
}

// Avoid copying large structs
func (b *BigStruct) process() {
    // ...
}

Consistency

If any method needs a pointer receiver, use pointer receivers for all methods on that type

Interface satisfaction

Be mindful that *T and T are different types when implementing interfaces

Method sets and interfaces

Pointer receivers and value receivers have different method sets:
  • A value of type T can only call methods with value receivers
  • A value of type *T can call methods with both pointer and value receivers
This matters when implementing interfaces!
type Shaper interface {
    area() int
}

// If area() has a pointer receiver (*rect)
func (r *rect) area() int { return r.width * r.height }

// Then only *rect implements Shaper, not rect
var s Shaper = &rect{width: 10, height: 5}  // ✓ Works
// var s Shaper = rect{width: 10, height: 5} // ✗ Compile error

Best practices

If you have one pointer receiver method, make all methods use pointer receivers for consistency.
When in doubt, use pointer receivers. They’re more flexible and avoid copying overhead.
Use value receivers for small structs that shouldn’t be modified (like time.Time).
Use short, consistent names for receivers. Common convention is to use a 1-2 letter abbreviation of the type name:
func (r *rect) area() int    // 'r' for rect
func (p *person) greet()     // 'p' for person

Structs

Learn about struct types

Interfaces

Use methods with interfaces

Pointers

Understanding pointers in Go

Build docs developers (and LLMs) love