Methods (value vs pointer receivers)

A method is a function bound to a type — it's how you attach behaviour to your data. The single most important decision is the receiver : a value receiver works on a copy, a pointer receiver works on the original. Get that right and your types behave exactly as you intend; this lesson makes the choice clear.

Learn Methods (value vs pointer receivers) in our free Go course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…

Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Defining a Method

A method is just a function with an extra receiver written in parentheses before the name: func (r Rectangle) Area() float64 . Inside the method, the receiver ( r ) gives you access to the value's fields. You call it with familiar dot notation — box.Area() — which keeps related data and behaviour neatly together.

2️⃣ Value vs Pointer Receivers

This is the heart of the lesson. A value receiver ( (c Counter) ) operates on a copy — any changes it makes are thrown away when the method returns. A pointer receiver ( (c *Counter) ) operates on the original, so mutations stick. The good news: Go automatically takes the address for you, so you still call c.Inc() even though Inc has a pointer receiver.

3️⃣ Methods on Any Named Type

Methods aren't limited to structs — you can attach them to any named type you define , like type Celsius float64 . One special method is worth knowing now: define String() string on your type and the fmt package will use it automatically whenever it prints your value. This is your first taste of how Go's interfaces work under the hood.

Now make a method that actually changes the wallet — which receiver do you need?

These lines define a pointer-receiver method and call it, but they're scrambled. Put them in order so that, with a type Counter struct {' '} already declared, the code compiles and c.Count ends at 1.

Why: the method declaration is one unit — its header func (c *Counter) Inc() {' , the body c.Count++ , and the closing brace must sit together. Separately, inside main you must create the value ( c := Counter{' '} ) before calling c.Inc() , which increments Count from 0 to 1 through the pointer.

Rule of thumb: mutates state or large struct → pointer receiver; small, read-only → value receiver (but stay consistent across the type).

Predict the output before you reveal the answer.

{'func (c Counter) Inc() ; c := Counter ; c.Inc(); fmt.Println(c.Count)'}

0 — a value receiver gets a copy, so Count++ never touches the original. Use (c *Counter) to make it 1.

{'func (c *Counter) Inc() ; c := Counter ; c.Inc(); c.Inc(); fmt.Println(c.Count)'}

2 — a pointer receiver mutates the original, and Go auto-takes the address, so both calls stick.

{'type C float64; func (c C) String() string ; fmt.Println(C(5))'}

cold — because C has a String() method, fmt.Println calls it instead of printing the raw number.

Q: Value receiver or pointer receiver — how do I choose?

Use a pointer receiver if the method modifies the value or if the struct is large (to avoid copying). Use a value receiver for small, immutable types. When in doubt, prefer a pointer receiver — and keep all methods on a type consistent.

Q: Do I have to write (&c).Inc() for pointer methods?

No — Go automatically takes the address of an addressable value, so c.Inc() just works. The convenience mirrors how c.Field works through a struct pointer.

It satisfies the fmt.Stringer interface, so the fmt package calls it automatically when printing your value. It's a preview of how interfaces let types opt into behaviour — your next lesson.

Add Deposit and Withdraw methods. They must change the balance, so think carefully about the receiver type. Withdraw should refuse when funds are short. Write it yourself and match the example output.

Practice quiz

What is a Go method?

  • A standalone function
  • A function with a receiver
  • A struct field
  • A package

Answer: A function with a receiver. A method is a function with a receiver argument, called with dot notation.

What does a value receiver operate on?

  • The original value
  • A copy of the value
  • A pointer always
  • Nothing

Answer: A copy of the value. A value receiver gets a copy, so mutations inside it do not affect the original.

Which receiver type lets a method mutate the original?

  • Value receiver
  • Pointer receiver
  • Neither
  • Both equally

Answer: Pointer receiver. A pointer receiver (*T) can modify the value the caller holds.

Given func (c Counter) Inc(), does calling c.Inc() change c?

  • Yes
  • No, it works on a copy
  • Only sometimes
  • It panics

Answer: No, it works on a copy. A value receiver copies c, so Inc() cannot change the caller's variable.

Given func (c *Counter) Inc(), can you call it on an addressable value c?

  • No, only on pointers
  • Yes — Go auto-takes &c
  • Only with new()
  • Only inside main

Answer: Yes — Go auto-takes &c. Go automatically takes the address, so c.Inc() works for a pointer-receiver method on an addressable c.

Can methods be defined on types other than structs?

  • No, structs only
  • Yes, on any named type
  • Only on slices
  • Only on interfaces

Answer: Yes, on any named type. Methods can attach to any named type, including types based on int, string, slices, etc.

Which method customises how fmt prints a type?

  • Print()
  • String() string
  • Format()
  • ToString()

Answer: String() string. Implementing String() string (fmt.Stringer) controls how fmt prints the value.

Why prefer a pointer receiver for a method that updates a field?

  • It is faster to type
  • So the change persists on the original
  • It is required by Go
  • To avoid imports

Answer: So the change persists on the original. Only a pointer receiver writes back to the caller's value; a value receiver edits a throwaway copy.

What is the receiver in func (c Counter) Inc()?

  • Inc
  • Counter
  • c of type Counter
  • c of type *Counter

Answer: c of type Counter. The receiver is c, whose type is Counter (a value receiver).

How is a method called on a variable c?

  • call(c, Inc)
  • Inc(c)
  • c.Inc()
  • c->Inc()

Answer: c.Inc(). Methods use dot notation: c.Inc().