In the previous lesson, we learned the basics of pointers. Now let's explore how pointers are essential when you need to modify complex data structures like structs inside functions.
When you pass a struct to a function, Go creates a complete copy of that struct:
type Fighter struct {
Name string
PowerLevel int
}
func powerUp(fighter Fighter) {
fighter.PowerLevel = fighter.PowerLevel * 2
fmt.Println("Inside function:", fighter.PowerLevel)
}
func main() {
goku := Fighter{
Name: "Goku",
PowerLevel: 9000,
}
fmt.Println("Before:", goku.PowerLevel) // Output: Before: 9000
powerUp(goku) // Output: Inside function: 18000
fmt.Println("After:", goku.PowerLevel) // Output: After: 9000
}
Even though we doubled the power level inside the function, the original goku struct remained unchanged!
To modify the original struct, we need to pass a pointer to it:
type Fighter struct {
Name string
PowerLevel int
}
func powerUp(fighter *Fighter) {
fighter.PowerLevel = fighter.PowerLevel * 2
fmt.Println("Inside function:", fighter.PowerLevel)
}
func main() {
goku := Fighter{
Name: "Goku",
PowerLevel: 9000,
}
fmt.Println("Before:", goku.PowerLevel) // Output: Before: 9000
powerUp(&goku
Notice the changes:
*Fighter (pointer to Fighter)&goku (address of goku)Go makes working with struct pointers easier by automatically dereferencing them when accessing fields:
func powerUp(fighter *Fighter) {
// Both of these work the same way:
fighter.PowerLevel = fighter.PowerLevel * 2
(*fighter).PowerLevel = (*fighter).PowerLevel * 2 // More explicit
// Go automatically dereferences, so you can use the simpler syntax
}
You don't need to write (*fighter).PowerLevel - just fighter.PowerLevel works!
type Fighter struct {
Name string
PowerLevel int
}
func kaioken(fighter *Fighter) {
fighter.PowerLevel = fighter.PowerLevel * 5
}
func main() {
goku := Fighter{
Name: "Goku",
PowerLevel: 9001,
}
fmt.Println("Old power level:", goku.PowerLevel) // Output: 9001
kaioken(&goku)
fmt.Println("New power level:", goku.PowerLevel
You can modify any field of a struct through a pointer:
type Character struct {
Name string
Health int
Mana int
}
func heal(character *Character) {
character.Health = 100
character.Mana = 50
fmt.Println(character.Name, "has been healed!")
}
func main() {
player := Character{
Name: "Hero",
Health: 20,
Mana: 5,
}
fmt.Println("Before:", playerHealth playerMana
For simple read operations, you don't need pointers:
type Fighter struct {
Name string
PowerLevel int
}
// No pointer needed - we're just reading
func displayStats(fighter Fighter) {
fmt.Println(fighter.Name, "has power level", fighter.PowerLevel)
}
// Pointer needed - we're modifying
func train(fighter *Fighter) {
fighter.PowerLevel = fighter.PowerLevel + 1000
}
Pointers can also improve performance with large structs, since copying a pointer (just a memory address) is faster than copying an entire large struct. However, for most cases, only use pointers when you need to modify values - clarity is more important than micro-optimizations.
For this challenge, create a struct called BankAccount with the following fields:
Owner (string)Balance (int)Then create a function called deposit that accepts a pointer to a BankAccount and an amount (int).
The function should:
amount to the account's Balance"Deposited [amount] to [owner]"