Slices
By Syncfusion®, Inc. · Last updated
Go slices: growable lists built on arrays. Create them, append, copy, len and cap, the underlying array, slicing, removing and inserting, and arrays vs slices.
What Are Slices?
Slice = a list of values of one type that can grow and shrink.
- It looks like an array, but without a size in the brackets:
[]int. - You can add items with
append. - Behind every slice, Go keeps an array to hold the values.
Slices are the list you will use most in Go.
Simple lesson: a slice is a flexible list that grows when you add items.
A Real-World Example
Think of the class library's issue register.
- On Monday, 3 students borrow books. The list has 3 names.
- On Tuesday, 2 more borrow. The list grows to 5.
- When the page is full, the librarian moves to a bigger page and copies the names across.
Now the mapping:
- The list of names = the slice.
- How many names are written = the slice's length.
- How many lines the page has = the slice's capacity.
- Moving to a bigger page = Go making a new, bigger array when the old one is full.
You met arrays in Arrays.
- An array has a fixed size:
[5]int. - A slice can grow:
[]int. - A slice is a small window onto an array.
What You Will Learn
In this lesson, you will learn:
- What slices are, why they are used, and how to create them.
- How to use
append,copy,lenandcap. - What length, capacity and the underlying array are.
- How to slice, reslice, remove and insert elements.
- The differences between arrays and slices, and when to use each.
Slice Fundamentals
Why Slices Are Used
- The size is not known in advance: students joining a club, marks typed in.
- The list grows while the program runs.
- Functions can take a slice of any length.
- Most Go packages give and take slices, like
strings.Split.
Slice Literals
Slice literal = a slice written out with its values.
A slice literal
package main
import "fmt"
func main() {
marks := []int{91, 78, 85}
names := []string{"Asha", "Ravi"}
fmt.Println(marks, len(marks))
fmt.Println(names, len(names))
}
[91 78 85] 3
[Asha Ravi] 2
[]inthas no number inside the brackets. That makes it a slice.[3]intwould be an array.
Creating Slices
Four ways to make a slice
make and nil slices
package main
import "fmt"
func main() {
a := make([]int, 3)
b := make([]int, 0, 10)
var c []int
fmt.Println(a, len(a), cap(a))
fmt.Println(b, len(b), cap(b))
fmt.Println(c, len(c), c == nil)
}
[0 0 0] 3 3
[] 0 10
[] 0 true
make([]int, 3)gives three zeros, ready to use.make([]int, 0, 10)is empty, but has room for 10 without growing.- A nil slice prints as
[]and has length 0.
Simple lesson: write []T{...} or use make to create a slice. With no number in the brackets, it is a slice, not an array.
Working with Slices
append()
append = add values to the end of a slice. It returns the new slice.
append
package main
import "fmt"
func main() {
var club []string
club = append(club, "Asha")
club = append(club, "Ravi", "Meena")
more := []string{"Kabir", "Priya"}
club = append(club, more...)
fmt.Println(club, len(club))
}
[Asha Ravi Meena Kabir Priya] 5
- Always write
club = append(club, ...). The result must be stored. - You can add many values at once.
more...adds every element of another slice.
len() and cap()
len(s)= how many elements the slice has now.cap(s)= how many elements fit in the array behind it, before Go must make a bigger one.
Press append again and again. Watch the blue boxes and the dashed ones.
Look at what happens when the slice is full:
- len = cap: there is no room.
- Go makes a new array, usually about twice as big, and copies the values.
- So
capjumps: 1, 2, 4, 8, 16.
Watch the capacity grow
package main
import "fmt"
func main() {
var s []int
for i := 1; i <= 5; i++ {
s = append(s, i*10)
fmt.Println(s, "len", len(s), "cap", cap(s))
}
}
[10] len 1 cap 1
[10 20] len 2 cap 2
[10 20 30] len 3 cap 4
[10 20 30 40] len 4 cap 4
[10 20 30 40 50] len 5 cap 8
- The exact growth is chosen by Go, and it may change between versions.
- Do not write code that depends on the exact capacity.
append on a nil slice is fine: it makes the array for you.
But reading s[0] on an empty or nil slice is a panic. Check len(s) > 0 first.
copy()
copy = copy elements from one slice into another. It returns how many it copied.
copy
package main
import "fmt"
func main() {
original := []int{91, 78, 85}
backup := make([]int, len(original))
n := copy(backup, original)
backup[0] = 0
fmt.Println(original, backup, n)
}
[91 78 85] [0 78 85] 3
copy(destination, source): the target comes first.- It copies only as many as fit in the smaller slice.
backuphas its own array now, so changing it does not changeoriginal.
Simple lesson: append adds and returns a new slice, copy makes a separate copy, and len and cap tell you size and room.
Slice Internals
What a Slice Really Is
A slice is a small header with three parts:
- pointer = where its first element is in the array
- length = how many elements you can use
- capacity = how many elements fit, from the start to the end of the array
Underlying Array
Underlying array = the array that really holds a slice's values.
Two slices can share one underlying array.
Move the sliders, then press part[0]++.
Look at two things:
- The blue boxes are the part of the array that
partcan see. - Changing
part[0]changes the same box innums.
Two slices share one array
package main
import "fmt"
func main() {
nums := []int{10, 20, 30, 40, 50}
part := nums[1:3]
part[0] = 99
fmt.Println(nums)
fmt.Println(part, len(part), cap(part))
}
[10 99 30 40 50]
[99 30] 2 4
partstarts atnums[1], sopart[0]is the same box asnums[1].- Its capacity is 4: from
nums[1]to the end of the array.
When append Surprises You
append can write into a shared array
- When a slice shares an array and has spare capacity,
appendwrites into that array. - To keep them apart, make a real copy first with
copy.
Simple lesson: a slice is a window onto an array, and slices that share an array share their changes.
Slice Operations
Slicing
Slicing = taking part of a slice or array: s[low:high].
lowis included.highis not.s[:3]= from the start up to 3.s[2:]= from 2 to the end.s[:]= all of it.
Slicing
package main
import "fmt"
func main() {
days := []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
fmt.Println(days[1:4])
fmt.Println(days[:2])
fmt.Println(days[4:])
}
[Tue Wed Thu]
[Mon Tue]
[Fri Sat]
Reslicing
Reslicing = slicing a slice again. It is fast, because no values are copied.
Reslice to drop the first and last
package main
import "fmt"
func main() {
queue := []string{"Asha", "Ravi", "Meena", "Kabir"}
queue = queue[1:]
fmt.Println(queue)
queue = queue[:len(queue)-1]
fmt.Println(queue)
}
[Ravi Meena Kabir]
[Ravi Meena]
queue[1:]drops the first element, like serving the first person in a line.queue[:len(queue)-1]drops the last element.
Removing Elements
To remove the element at index i, join the parts before and after it.
Remove an element
package main
import "fmt"
func main() {
names := []string{"Asha", "Ravi", "Meena", "Kabir"}
i := 1
names = append(names[:i], names[i+1:]...)
fmt.Println(names, len(names))
}
[Asha Meena Kabir] 3
names[:i]= everything before index 1.names[i+1:]...= everything after it.appendjoins them, so"Ravi"is gone.
Inserting Elements
To put a value at index i, make room and move the rest right.
Insert an element
package main
import "fmt"
func main() {
line := []string{"Asha", "Meena", "Kabir"}
i := 1
line = append(line, "")
copy(line[i+1:], line[i:])
line[i] = "Ravi"
fmt.Println(line)
}
[Asha Ravi Meena Kabir]
append(line, "")adds one empty slot at the end.copymoves everything fromione step to the right.- Then we put
"Ravi"into the free slot.
Easy way to remember
- Remove at
i:s = append(s[:i], s[i+1:]...) - Insert at
i: grow by one,copy(s[i+1:], s[i:]), thens[i] = v - Go 1.21+ also has
slices.Deleteandslices.Insert. This lab does not run theslicespackage.
Simple lesson: s[low:high] takes a part, and append with slicing removes or inserts elements.
Arrays vs Slices
Differences
| Array | Slice | |
|---|---|---|
| Written as | [5]int | []int |
| Size | Fixed, part of the type | Can grow with append |
Assigning b := a | Copies every element | Copies the header. Both share the array |
| Passing to a function | The function gets a copy | The function can change the elements |
Compare with == | Yes | No, only with nil |
| Zero value | Full of zero values | nil |
Memory Behavior
A function can change a slice's elements
package main
import "fmt"
func addGrace(marks []int) {
for i := range marks {
marks[i] += 5
}
}
func main() {
marks := []int{40, 60, 80}
addGrace(marks)
fmt.Println(marks)
}
[45 65 85]
- The function gets a copy of the header, but both headers point to the same array.
- So the changes are seen by the caller.
- With an array, the function would change only its own copy. See Arrays.
When to Use Each
- Use a slice almost always: lists of students, marks, words, lines of a file.
- Use an array when the size is truly fixed and small: the 7 days of a week, an RGB colour
[3]uint8. - Use an array when you need to compare with
==or use it as a map key.
Simple lesson: use slices for lists that change, and arrays only for small, fixed-size groups.
TL;DR
- Slice = a growable list:
[]int. An array holds its values. - Create =
[]int{1, 2},make([]int, len, cap),var s []int(nil),arr[1:3]. append =
s = append(s, v). Always store the result.append(s, other...)adds a whole slice.
len = elements in use. cap = room in the array.
- Full → Go makes a bigger array (often double).
- copy(dst, src) = a separate copy.
- Slicing =
s[low:high], low included, high not. It shares the array. - Remove =
append(s[:i], s[i+1:]...). Insert = grow,copy, set. - Array vs slice = fixed and copied vs growable and shared.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
append(s, 5) without s = | append changes s in place | It returns the new slice | The new value is lost. |
b := a to make a backup | It copies the values | It shares the array | Changing b changes a. Use copy. |
s[len(s)] | The last index is len | It is len - 1 | index out of range |
s[2:5] on a 4-element slice | Slicing past the end is fine | high must be ≤ cap | slice bounds out of range |
Writing to make([]int, 0, 5)[0] | cap gives usable boxes | Only len boxes are usable | index out of range |
a == b for two slices | Slices compare like arrays | Only == nil is allowed | A build error. |
Depending on exact cap growth | Growth is always ×2 | Go chooses, and it may change | Code breaks on another version. |
Check Yourself
Exercises
What to Remember
- A slice is a growable list:
[]T. An array holds its values. - Always write
s = append(s, ...). lenis what you use.capis the room before Go must grow the array.s[low:high]shares the array. Usecopyfor a separate copy.- Remove with
append(s[:i], s[i+1:]...). - Use slices for most lists. Use arrays only for small, fixed groups.
Next: Maps. You will learn how to store values by a key, like marks by student name.