ProgramGuru

Value vs Reference Semantics

Start with Value vs Reference Semantics

Go passes function arguments by value. Whether a mutation is visible elsewhere depends on the kind of value that was copied and what storage it refers to.

What you’ll learn

  • Explain the main purpose of value vs reference semantics.
  • Read and run the complete Go example in this lesson.
  • Apply the concept and recognize common mistakes.

Understand the idea

An int argument is copied, so changing the parameter leaves the caller’s integer alone. A slice value contains a view of a backing array; passing a slice copies that view, so changing an existing element is normally visible through other views of the same array.

Maps similarly refer to map storage; assigning a map variable or passing one does not clone all entries. Pointers copy an address while sharing its target. Do not describe Go as having a second pass-by-reference argument mode: it does not.

Visualize how it works

Read the labels and compare the identities, types and storage relationships shown here.

Three copied argument values, different shared storagePassing an int makes an independent integer parameter. Passing a slice copies its header but both views refer to one backing array. Passing a map copies a map value while both variables refer to the same map entries.Every argument is passed by valueint argumentCaller: 1Parameter copy: 1Assign 99 to copyCaller remains 1slice argumentCopy slice headerShared backing arrayWrite values[0]Caller sees element editmap argumentCopy map valueShared map storageSet labels["a"]Caller sees entry editCopying a value is not always the same as copying its referred-to storage.
The relationships in this diagram match the lesson’s Go code.

Run a complete Go example

This example is a complete program. Save it as main.go or paste it into the Go Playground.

Go code

package main

import "fmt"

func edit(n int, values []int, labels map[string]int) {
	n = 99
	values[0] = 99
	labels["a"] = 99
}
func main() {
	n := 1
	values := []int{1, 2}
	labels := map[string]int{"a": 1}
	edit(n, values, labels)
	fmt.Println("Number:", n)
	fmt.Println("Slice:", values)
	fmt.Println("Map entry:", labels["a"])
}

Output

Number: 1
Slice: [99 2]
Map entry: 99

Trace what the program does

All three arguments are copied into parameters. The standalone integer remains 1, but the slice’s shared backing array element changes to 99, and the shared map entry changes to 99.

Avoid common mistakes

  • Go does not have pass-by-reference parameters as a distinct calling convention.
  • Appending to a copied slice header can reallocate storage and does not automatically update the caller’s slice length.

Try it yourself

Modify the function to append to values, then explain why the caller must receive the returned slice if it needs the new length.

Tip: Edit the example above, predict the output, and run the modified code to test your reasoning.

Check your understanding

Why can editing values[0] in a function affect the caller?
Choose one answer. You can try again.

Answer and explanation

A copied slice header still refers to shared backing storage

Correct. All three arguments are copied into parameters.

What you should remember

You have practiced Value vs Reference Semantics in a small, self-contained program. The important habit is to predict what runs, inspect the actual output and distinguish the language rule from the particular example.

Before continuing, make sure you can explain the program to someone who has not seen it, including the line that changes its result.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Understanding nil →