Pass by Value vs Reference
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 21 of 107.
Go is a pass-by-value language. When you pass a variable to a function, Go creates a copy of that value. This means changes inside the function don't affect the original variable.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (unchanged)
}The same applies to structs. Passing a struct to a function copies all its fields, so modifications inside the function are lost.
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (unchanged)
}To modify the original value, pass a pointer instead. The pointer itself is copied, but it still points to the same memory address, allowing you to change the original data.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (modified!)
}Use pointers when you need to modify the original value or when passing large structs to avoid expensive copying. Use values when you want to protect the original data from unintended changes.
Challenge
EasyLet's build a shopping cart system that demonstrates the difference between passing structs by value versus by pointer. You'll see firsthand how only pointer parameters can modify the original data.
You'll organize your code across two files:
cart.go: Define aCartItemstruct withName(string),Price(float64), andQuantity(int) fields. Create two functions that attempt to update the quantity:UpdateQuantityByValuetakes aCartItem(by value) and a new quantity, then sets the item's quantity to the new valueUpdateQuantityByPointertakes a*CartItem(pointer) and a new quantity, then sets the item's quantity to the new value
Totalmethod onCartItemthat returns the price multiplied by quantity.main.go: Read item details from input, create a CartItem, then demonstrate both update approaches. First try updating with the value function and show the result, then update with the pointer function and show the result again.
The following inputs will be provided:
- Line 1: Item name
- Line 2: Price (float)
- Line 3: Initial quantity (integer)
- Line 4: New quantity to set (integer)
Output format:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]For example, given Laptop, 999.99, 1, and 3, your output should be:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97Notice how the quantity stays at 1 after the value update (the function modified a copy), but changes to 3 after the pointer update (the function modified the original). Format totals with two decimal places.
Cheat sheet
Go is a pass-by-value language. When you pass a variable to a function, Go creates a copy of that value, so changes inside the function don't affect the original variable.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (unchanged)
}The same applies to structs. Passing a struct to a function copies all its fields:
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (unchanged)
}To modify the original value, pass a pointer. The pointer itself is copied, but it still points to the same memory address:
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (modified!)
}Use pointers when you need to modify the original value or when passing large structs to avoid expensive copying. Use values when you want to protect the original data from unintended changes.
Try it yourself
package main
import (
"fmt"
)
func main() {
// Read input
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: Create a CartItem with the input values
// TODO: Print initial state using format:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: Try updating quantity using UpdateQuantityByValue
// Then print the result (notice quantity won't change)
// TODO: Update quantity using UpdateQuantityByPointer
// Then print the result (notice quantity will change)
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool