Module lessons (2/5)
Methods: value vs pointer receiver
A method in Go is a function with a receiver: a special
parameter declared between func and the method name, which binds the
method to a type. There are no classes: methods can be defined on
any named type declared in the current package.
Syntax
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"The receiver p Person makes Greet a method of Person. Inside the
method, p is a variable like any other.
Value receiver vs pointer receiver
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
return strings.ToUpper(p.Name)
}
// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
p.Name = n
}With a pointer receiver you can modify the receiver and the changes are visible outside the method. With a value receiver you cannot — you'd only modify the local copy.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visibleWhen to use a pointer receiver
Practical rules, in order of priority:
- You must modify the receiver → pointer receiver. Mandatory.
- The struct is large (dozens of fields, arrays inside) → pointer receiver to avoid expensive copies.
- Consistency: if AT LEAST one method on the type has a pointer receiver, use a pointer receiver for the others too. A very important convention for interfaces (Module 6).
- Small, immutable, "value" types (time.Time, complex numbers, small structs) → value receiver, more readable.
Methods on non-struct types
You can define methods on any NAMED type declared in your package:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68You cannot define them on types from other packages (e.g. int, string): you must
first create a local named type.
Try it
Add a Greet() string method (value receiver) to Person that returns 'ciao <Name>'.
Show hint
Receiver in parentheses BEFORE the method name.
Solution available after 3 attempts
Add a Rename(n string) method with a POINTER receiver that changes p.Name.
Show hint
Pointer receiver: `(p *Person)`.
Solution available after 3 attempts
When should you use a pointer receiver (p *T)?
func (t ?T) M() { ... }Recap
- Method = function with a receiver:
func (r T) Name(...) ... { ... }. - Value receiver: operates on a copy; Pointer receiver: operates on the original.
- Go performs the
T↔*Tconversion automatically at the call site. - Pointer receiver when: mutation + large struct + consistency with other methods of the type.
- Never mix value and pointer receivers on the same type.
- Methods go on NAMED types in the current package (including non-struct).