ProgramGuru

How Slices Work Internally

Start with How Slices Work Internally

A slice is a small descriptor pointing to an underlying array. Understanding that descriptor explains why changing a sub-slice affects another view and why append can sometimes break that sharing.

What you’ll learn

  • Explain the main purpose of how slices work internally.
  • Read and run the complete Go example in this lesson.
  • Apply the concept and recognize common mistakes.

Understand the idea

A slice describes a starting position in an array, a length and a capacity. Creating view := base[1:3] does not copy the selected elements. Both slices can observe writes to those shared positions.

If append fits inside the existing capacity, it can write into the shared backing array. If growth requires more capacity, it allocates a new array for the returned slice. Therefore, whether two slice values still share elements after append depends on capacity. Use copy or append([]T(nil), s...) when you need an independent element copy.

Visualize how it works

Read the labels and compare which positions are part of the visible value and which belong to the underlying representation.

A slice points to a region of an underlying arrayThe backing array contains 10, 20, 30 and 40. A slice starts at element index 1 and has length 2 and capacity 3. Positions 1 and 2 are highlighted as the slice's visible elements.Slice descriptor and shared elementsview := base[1:3]start → index 1 • len = 2 • cap = 3index 010index 120index 230index 340The green cells share their storage with base.Writing view[0] updates base[1].
This is the exact relationship illustrated by the Go example in this lesson.

Tools, syntax and practical notes

Further reading: Go Slices: usage and internals explains the slice descriptor, length, capacity and copy behavior with diagrams.

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 main() {
	base := []int{10, 20, 30, 40}
	view := base[1:3]
	view[0] = 99
	fmt.Println("Shared base:", base)
	cloned := append([]int(nil), view...)
	cloned[0] = 7
	fmt.Println("View:", view)
	fmt.Println("Clone:", cloned)
	fmt.Println("Base still:", base)
}

Output

Shared base: [10 99 30 40]
View: [99 30]
Clone: [7 30]
Base still: [10 99 30 40]

Trace what the program does

The view references base positions 1 and 2. Writing view[0] updates base[1] to 99. Appending the view into a nil slice constructs an independent copy; changing cloned[0] to 7 does not change the view or base.

Avoid common mistakes

  • Do not assume every append reallocates; it can reuse existing storage.
  • Sharing a backing array may retain a larger allocation than intended when a tiny slice remains alive.

Try it yourself

Create left := base[:2] and right := base[1:3]. Change left[1]; determine which right element changes.

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

Check your understanding

When do two slices created by reslicing generally share elements?
Choose one answer. You can try again.

Answer and explanation

When they reference overlapping positions of the same backing array

Correct. The view references base positions 1 and 2.

What you should remember

You have practiced How Slices Work Internally 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: Multidimensional Slices →