Pointers vs Values: When to Use Which
When Mutation Is Needed
If a function or method is meant to change the caller's original data, it must take a pointer -- passing by value only ever operates on a copy, so any changes are lost once the function returns.
Example: When Mutation Is Needed
package main
import "fmt"
type Account struct {
Balance float64
}
func (a *Account) Withdraw(amount float64) {
a.Balance -= amount
}
func main() {
acc := Account{Balance: 200}
acc.Withdraw(50)
fmt.Println(acc.Balance)
}
Login to try C/C++/Java/PHP code in the editor
When Copying Is Fine
For small, simple values -- like a Point with two ints -- copying is cheap and often makes code easier to reason about, since the caller doesn't need to worry about unexpected mutation through a shared pointer.
Example: When Copying Is Fine
package main
import "fmt"
type Point struct {
X, Y int
}
func translate(p Point, dx, dy int) Point {
return Point{X: p.X + dx, Y: p.Y + dy}
}
func main() {
original := Point{1, 1}
moved := translate(original, 2, 3)
fmt.Println("original:", original, "moved:", moved)
}
Login to try C/C++/Java/PHP code in the editor
Avoiding Copies of Large Structs
For a struct with many fields, passing it by value copies every field on every function call, which adds up in hot code paths -- passing a pointer instead avoids that copying cost, at the price of sharing mutable state.
Note: A rule of thumb: use pointers for structs larger than a few machine words, or whenever the struct might grow.
Example: Avoiding Copies of Large Structs
package main
import "fmt"
type Report struct {
Title string
Lines []string
Total float64
}
func summarize(r *Report) string {
return fmt.Sprintf("%s: %d lines, total %.2f", r.Title, len(r.Lines), r.Total)
}
func main() {
r := &Report{Title: "Sales", Lines: []string{"a", "b"}, Total: 99.5}
fmt.Println(summarize(r))
}
Login to try C/C++/Java/PHP code in the editor
- Defaulting to pointers everywhere for performance on tiny structs, when copying a small value is often just as fast and simpler to reason about.
- Mixing value and pointer receivers on the same type's methods inconsistently, which can cause confusing behavior with interfaces.
- Passing a large struct by value in a hot loop, causing unnecessary copying overhead that a pointer would avoid.
- Use a pointer when a function needs to modify the caller's original value.
- Use a pointer to avoid copying large structs, improving performance.
- Use a plain value for small, simple data where mutation isn't needed -- it's simpler to reason about.
- Keep receiver types consistent across a single type's methods, favoring pointer receivers if any method needs to mutate.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: