Deleting and comparing
Two slice operations that look like they should work but do not, unless you know where to look. Both are cleanly solved by the slices package, Go's generic slice-helper library introduced in Go 1.21. That package is what you reach for in modern Go code; this lesson teaches it as the default and shows the hand-rolled equivalent underneath so the mental model is complete.
Deleting an element
Slices do not have a .remove(i) method. To remove an element at a given index in modern Go, you use slices.Delete:
import "slices"
nums := []int{10, 20, 30, 40, 50}
nums = slices.Delete(nums, 1, 2) // remove indices [1:2), i.e. just index 1
fmt.Println(nums) // [10 30 40 50]
slices.Delete(s, i, j) removes the elements in the half-open range [i, j) and returns a new slice header with the remaining elements. The range convention is the same as the slice expression s[i:j] you met in the Slicing lesson. To delete one element at index i, pass i and i+1.
Like append, you have to assign the result back. The function shifts elements in place and adjusts the length of the header, and the original variable still holds the old length. Dropping the return value leaves the "before" length in place, which is the same bug shape as append and equally silent.
What slices.Delete actually does
Under the hood, slices.Delete uses the shift-and-truncate pattern from the copy lesson. Written by hand it looks like this:
// Equivalent to slices.Delete(nums, i, i+1):
copy(nums[i:], nums[i+1:])
nums = nums[:len(nums)-1]
copy shifts every element after i one position to the left, overwriting the element at i. The last slot now holds a duplicate of the previous last element (the shift left one of its copies). Re-slicing to len(nums)-1 drops that duplicate and produces the final slice.
A shorter equivalent uses append to splice the two halves back together:
// Also equivalent, more compact:
nums = append(nums[:i], nums[i+1:]...)
Both forms predate slices.Delete and still work. Reach for slices.Delete in new code; recognise the hand-rolled forms when reading older code.
In current Go, slices.Delete zeroes the trailing slots it removes. That means deleted pointer values do not stay behind in the underlying array keeping other objects alive. If you use one of the hand-rolled delete forms below instead, remember that they do not do that cleanup for you automatically.
Comparing slices
== does not work between two slices. Go deliberately refuses:
a := []int{1, 2, 3}
b := []int{1, 2, 3}
fmt.Println(a == b)
// compile error: invalid operation: a == b (slice can only be compared to nil)
The only slice comparison == allows is against nil:
var s []int
fmt.Println(s == nil) // true
The reason is less obvious than it looks. A slice is a three-field header (pointer, length, capacity), so "are these the same header" and "do these hold the same values" are two different questions. Go could have picked either, so it picked neither, and asks you to say which you mean.
For value equality, use slices.Equal:
import "slices"
a := []int{1, 2, 3}
b := []int{1, 2, 3}
c := []int{1, 2, 3, 4}
fmt.Println(slices.Equal(a, b)) // true
fmt.Println(slices.Equal(a, c)) // false
slices.Equal compares element-by-element. Two slices are equal when they have the same length and every index holds an equal value.
In normal application code, value equality is usually the question that matters. Whether two slices share storage is mainly useful when you are debugging aliasing bugs like the ones from the previous lessons.
slices by defaultThe slices package (slices.Contains, slices.Index, slices.Sort, slices.Delete, slices.Equal, slices.Clone, slices.Insert, slices.Compact) is the modern, generic, standard-library way to work with slices. Almost every hand-rolled for-loop pattern you might write has a slices.X that does the same thing, clearer and shorter. The Standard library chapter covers the full set; for now, remember that import "slices" is where to look first.
The editor on this site has limited support for generic standard-library functions, so slices.Delete, slices.Equal, and related helpers may not currently run here. The exercise uses the hand-rolled forms instead so you can still run and verify it. On your own machine with a normal go build, prefer the slices.X forms shown above.
The starter declares nums := []int{10, 20, 30, 40, 50}. In main:
- Delete the element at index
1usingnums = append(nums[:1], nums[2:]...). - Print
numson its own line. You should see[10 30 40 50]. - Write a short loop that checks whether
20is still present innums. Print the result. You should seefalse.
[10 30 40 50] false